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

soot.toolkits.exceptions.ThrowableSet Maven / Gradle / Ivy

package soot.toolkits.exceptions;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2003 John Jorgensen
 * %%
 * 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 com.google.common.cache.CacheBuilder;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import soot.AnySubType;
import soot.FastHierarchy;
import soot.G;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.Singletons;
import soot.SootClass;
import soot.Unit;
import soot.options.Options;

/**
 * 

* A class for representing the set of exceptions that an instruction may throw. *

* *

* ThrowableSet does not implement the {@link java.util.Set} interface, so perhaps it is misnamed. Instead, it * provides only the operations that we require for determining whether a given statement might throw an exception that would * be caught by a given handler. *

* *

* There is a limitation on the combinations of operations permitted on a ThrowableSet. The * ThrowableSets returned by {@link #whichCatchableAs(RefType)} cannot be involved in subsequent * add() or whichCatchableAs() operations. That is, given * *

p = s.whichCatchableAs(r)
* * for any ThrowableSet s and {@link soot.RefType RefType} r, and * *
t == p.getUncaught() or t == p.getCaught()
* * then calls to t.add(r), t.add(a), and s.add(t), will throw an * {@link ThrowableSet.AlreadyHasExclusionsException}, for any RefType r, {@link AnySubType} * a, and ThrowableSet t. *

* *

* Actually the restrictions implemented are not quite so strict (there are some combinations of * whichCatchableAs() followed by add() which will not raise an exception), but a more accurate * description would require reference to the internals of the current implementation. The restrictions should not be too * onerous for ThrowableSet's anticipated uses: we expect ThrowableSets to grow by accumulating all * the exception types that a given {@link Unit} may throw, then, shrink as the types caught by different exception handlers * are removed to yield the sets representing exceptions which escape those handlers. *

* *

* The ThrowableSet class is intended to be immutable (hence the final modifier on its * declaration). It does not take the step of guaranteeing immutability by cloning the RefLikeType objects it * contains, though, because we trust {@link Scene} to enforce the existence of only one RefLikeType instance * with a given name. *

*/ public class ThrowableSet { private static final boolean INSTRUMENTING = false; private final static SootClass JAVA_LANG_OBJECT_CLASS = Scene.v().getObjectType().getSootClass(); /** * Set of exception types included within the set. */ protected final Set exceptionsIncluded; /** * Set of exception types which, though members of exceptionsIncluded, are to be excluded from the types represented by * this ThrowableSet. To simplify the implementation, once a ThrowableSet has any excluded types, * the various add() methods of this class must bar additions of subtypes of those excluded types. */ protected final Set exceptionsExcluded; /** * A map from ({@link RefLikeType} \\union ThrowableSet) to ThrowableSet. If the mapping (k,v) is * in memoizedAdds and k is a ThrowableSet, then v is the set that results from adding all * elements in k to this. If (k,v) is in memoizedAdds and k is a {@link RefLikeType}, then v is * the set that results from adding k to this. */ protected Map memoizedAdds; /** * Constructs a ThrowableSet which contains the exception types represented in include, except * for those which are also in exclude. The constructor is private to ensure that the only way to get a new * ThrowableSet is by adding elements to or removing them from an existing set. * * @param include * The set of {@link RefType} and {@link AnySubType} objects representing the types to be included in the set. * @param exclude * The set of {@link AnySubType} objects representing the types to be excluded from the set. */ protected ThrowableSet(Set include, Set exclude) { exceptionsIncluded = getImmutable(include); exceptionsExcluded = getImmutable(exclude); // We don't need to clone include and exclude to guarantee // immutability since ThrowableSet(Set,Set) is private to this // class, where it is only called (via // Manager.v().registerSetIfNew()) with arguments which the // callers do not subsequently modify. } private static Set getImmutable(Set in) { if ((null == in) || in.isEmpty()) { return Collections.emptySet(); } if (1 == in.size()) { return Collections.singleton(in.iterator().next()); } return Collections.unmodifiableSet(in); } /** * Returns an {@link Iterator} over a {@link Collection} of Throwable types which iterates over its elements in a * consistent order (maintaining an ordering that is consistent across different runs makes it easier to compare sets * generated by different implementations of the CFG classes). * * @param coll * The collection to iterate over. * * @return An iterator which presents the elements of coll in order. */ private static Iterator sortedThrowableIterator(Collection coll) { if (coll.size() <= 1) { return coll.iterator(); } else { @SuppressWarnings("unchecked") T[] array = (T[]) coll.toArray(new RefLikeType[coll.size()]); Arrays.sort(array, new ThrowableComparator()); return Arrays.asList(array).iterator(); } } private ThrowableSet getMemoizedAdds(Object key) { return memoizedAdds == null ? null : memoizedAdds.get(key); } private void addToMemoizedAdds(Object key, ThrowableSet value) { if (memoizedAdds == null) { memoizedAdds = new HashMap<>(); } memoizedAdds.put(key, value); } /** * Returns a ThrowableSet which contains e in addition to the exceptions in this * ThrowableSet. * *

* Add e as a {@link RefType} when you know that the run-time class of the exception you are representing is * necessarily e and cannot be a subclass of e. * *

* For example, if you were recording the type of the exception thrown by * *

   * throw new IOException("Permission denied");
   * 
* * you would call * *
   * add(Scene.v().getRefType("java.lang.Exception.IOException"))
   * 
* * since the class of the exception is necessarily IOException. * * @param e * the exception class * * @return a set containing e as well as the exceptions in this set. * * @throws {@link * ThrowableSet.IllegalStateException} if this ThrowableSet is the result of a * {@link #whichCatchableAs(RefType)} operation and, thus, unable to represent the addition of e. */ public ThrowableSet add(RefType e) throws ThrowableSet.AlreadyHasExclusionsException { if (INSTRUMENTING) { Manager.v().addsOfRefType++; } if (this.exceptionsIncluded.contains(e)) { if (INSTRUMENTING) { Manager.v().addsInclusionFromMap++; Manager.v().addsExclusionWithoutSearch++; } return this; } ThrowableSet result = getMemoizedAdds(e); if (result != null) { if (INSTRUMENTING) { Manager.v().addsInclusionFromMemo++; Manager.v().addsExclusionWithoutSearch++; } return result; } if (INSTRUMENTING) { Manager.v().addsInclusionFromSearch++; if (exceptionsExcluded.isEmpty()) { Manager.v().addsExclusionWithoutSearch++; } else { Manager.v().addsExclusionWithSearch++; } } FastHierarchy hierarchy = Scene.v().getOrMakeFastHierarchy(); boolean eHasNoHierarchy = hasNoHierarchy(e); for (AnySubType excludedType : exceptionsExcluded) { RefType exclusionBase = excludedType.getBase(); if ((eHasNoHierarchy && exclusionBase.equals(e)) || (!eHasNoHierarchy && hierarchy.canStoreType(e, exclusionBase))) { throw new AlreadyHasExclusionsException("ThrowableSet.add(RefType): adding" + e.toString() + " to the set [ " + this.toString() + "] where " + exclusionBase.toString() + " is excluded."); } } // If this is a real class, we need to check whether we already have it // in the list through subtyping. if (!eHasNoHierarchy) { for (RefLikeType incumbent : exceptionsIncluded) { if (incumbent instanceof AnySubType) { // Need to use incumbent.getBase() because // hierarchy.canStoreType() assumes that parent // is not an AnySubType. RefType incumbentBase = ((AnySubType) incumbent).getBase(); if (hierarchy.canStoreType(e, incumbentBase)) { addToMemoizedAdds(e, this); return this; } } else if (!(incumbent instanceof RefType)) { // assertion failure. throw new IllegalStateException( "ThrowableSet.add(RefType): Set element " + incumbent.toString() + " is neither a RefType nor an AnySubType."); } } } Set resultSet = new HashSet<>(this.exceptionsIncluded); resultSet.add(e); result = Manager.v().registerSetIfNew(resultSet, this.exceptionsExcluded); addToMemoizedAdds(e, result); return result; } private boolean hasNoHierarchy(RefType type) { final SootClass sootClass = type.getSootClass(); return !(sootClass.hasSuperclass() || JAVA_LANG_OBJECT_CLASS == sootClass); } /** * Returns a ThrowableSet which contains e and all of its subclasses as well as the exceptions in * this set. * *

* e should be an instance of {@link AnySubType} if you know that the compile-time type of the exception you * are representing is e, but the exception may be instantiated at run-time by a subclass of e. * *

* For example, if you were recording the type of the exception thrown by * *

   * catch (IOException e) {
   *    throw e;
   * }
   * 
* * you would call * *
   * add(AnySubtype.v(Scene.v().getRefType("java.lang.Exception.IOException")))
   * 
* * since the handler might rethrow any subclass of IOException. * * @param e * represents a subtree of the exception class hierarchy to add to this set. * * @return a set containing e and all its subclasses, as well as the exceptions represented by this set. * * @throws ThrowableSet.AlreadyHasExclusionsException * if this ThrowableSet is the result of a {@link #whichCatchableAs(RefType)} operation and, thus, * unable to represent the addition of e. */ public ThrowableSet add(AnySubType e) throws ThrowableSet.AlreadyHasExclusionsException { if (INSTRUMENTING) { Manager.v().addsOfAnySubType++; } ThrowableSet result = getMemoizedAdds(e); if (result != null) { if (INSTRUMENTING) { Manager.v().addsInclusionFromMemo++; Manager.v().addsExclusionWithoutSearch++; } return result; } // java.lang.Object is managed by the Scene -> guaranteed to only have one instance of the Object class final SootClass objectClass = Scene.v().getObjectType().getSootClass(); FastHierarchy hierarchy = Scene.v().getOrMakeFastHierarchy(); RefType newBase = e.getBase(); boolean newBaseHasNoHierarchy = hasNoHierarchy(newBase); if (INSTRUMENTING) { if (exceptionsExcluded.isEmpty()) { Manager.v().addsExclusionWithoutSearch++; } else { Manager.v().addsExclusionWithSearch++; } } for (AnySubType excludedType : exceptionsExcluded) { RefType exclusionBase = excludedType.getBase(); boolean exclusionBaseHasNoHierarchy = !(exclusionBase.getSootClass().hasSuperclass() || // exclusionBase.getSootClass() == objectClass); boolean isExcluded = exclusionBaseHasNoHierarchy && exclusionBase.equals(newBase); isExcluded |= !exclusionBaseHasNoHierarchy && (hierarchy.canStoreType(newBase, exclusionBase) || hierarchy.canStoreType(exclusionBase, newBase)); if (isExcluded) { if (INSTRUMENTING) { // To ensure that the subcategories total properly: Manager.v().addsInclusionInterrupted++; } throw new AlreadyHasExclusionsException("ThrowableSet.add(" + e.toString() + ") to the set [ " + this.toString() + "] where " + exclusionBase.toString() + " is excluded."); } } if (this.exceptionsIncluded.contains(e)) { if (INSTRUMENTING) { Manager.v().addsInclusionFromMap++; } return this; } if (INSTRUMENTING) { Manager.v().addsInclusionFromSearch++; } int changes = 0; boolean addNewException = true; Set resultSet = new HashSet<>(); for (RefLikeType incumbent : this.exceptionsIncluded) { if (incumbent instanceof RefType) { if (hierarchy.canStoreType(incumbent, newBase)) { // Omit incumbent from result. changes++; } else { resultSet.add(incumbent); } } else if (incumbent instanceof AnySubType) { RefType incumbentBase = ((AnySubType) incumbent).getBase(); if (newBaseHasNoHierarchy) { if (!incumbentBase.equals(newBase)) { resultSet.add(incumbent); } } // We have to use the base types in these hierarchy // calls // because we want to know if _all_ possible // types represented by e can be represented by // the incumbent, or vice versa. else if (hierarchy.canStoreType(newBase, incumbentBase)) { addNewException = false; resultSet.add(incumbent); } else if (hierarchy.canStoreType(incumbentBase, newBase)) { // Omit incumbent from result; changes++; } else { resultSet.add(incumbent); } } else { // assertion failure. throw new IllegalStateException("ThrowableSet.add(AnySubType): Set element " + incumbent.toString() + " is neither a RefType nor an AnySubType."); } } if (addNewException) { resultSet.add(e); changes++; } if (changes > 0) { result = Manager.v().registerSetIfNew(resultSet, this.exceptionsExcluded); } else { result = this; } addToMemoizedAdds(e, result); return result; } /** * Returns a ThrowableSet which contains all the exceptions in s in addition to those in this * ThrowableSet. * * @param s * set of exceptions to add to this set. * * @return the union of this set with s * * @throws ThrowableSet.AlreadyHasExclusionsException * if this ThrowableSet or s is the result of a {@link #whichCatchableAs(RefType)} * operation, so that it is not possible to represent the addition of s to this * ThrowableSet. */ public ThrowableSet add(ThrowableSet s) throws ThrowableSet.AlreadyHasExclusionsException { if (INSTRUMENTING) { Manager.v().addsOfSet++; } if ((exceptionsExcluded.size() > 0) || (s.exceptionsExcluded.size() > 0)) { throw new AlreadyHasExclusionsException( "ThrowableSet.Add(ThrowableSet): attempt to add to [" + this.toString() + "] after removals recorded."); } ThrowableSet result = getMemoizedAdds(s); if (result == null) { if (INSTRUMENTING) { Manager.v().addsInclusionFromSearch++; Manager.v().addsExclusionWithoutSearch++; } result = this.add(s.exceptionsIncluded); addToMemoizedAdds(s, result); } else if (INSTRUMENTING) { Manager.v().addsInclusionFromMemo++; Manager.v().addsExclusionWithoutSearch++; } return result; } /** * Returns a ThrowableSet which contains all the exceptions in addedExceptions in addition to * those in this ThrowableSet. * * @param addedExceptions * a set of {@link RefLikeType} and {@link AnySubType} objects to be added to the types included in this * ThrowableSet. * * @return a set containing all the addedExceptions as well as the exceptions in this set. */ private ThrowableSet add(Set addedExceptions) { Set resultSet = new HashSet<>(this.exceptionsIncluded); int changes = 0; FastHierarchy hierarchy = Scene.v().getOrMakeFastHierarchy(); // This algorithm is O(n m), where n and m are the sizes of the // two sets, so hope that the sets are small. for (RefLikeType newType : addedExceptions) { if (!resultSet.contains(newType)) { boolean addNewType = true; if (newType instanceof RefType) { for (RefLikeType incumbentType : resultSet) { if (incumbentType instanceof RefType) { if (newType == incumbentType) { // assertion failure. throw new IllegalStateException( "ThrowableSet.add(Set): resultSet.contains() failed to screen duplicate RefType " + newType); } } else if (incumbentType instanceof AnySubType) { RefType incumbentBase = ((AnySubType) incumbentType).getBase(); if (hierarchy.canStoreType(newType, incumbentBase)) { // No need to add this class. addNewType = false; } } else { // assertion failure. throw new IllegalStateException("ThrowableSet.add(Set): incumbent Set element " + incumbentType + " is neither a RefType nor an AnySubType."); } } } else if (newType instanceof AnySubType) { RefType newBase = ((AnySubType) newType).getBase(); for (Iterator j = resultSet.iterator(); j.hasNext();) { RefLikeType incumbentType = j.next(); if (incumbentType instanceof RefType) { RefType incumbentBase = (RefType) incumbentType; if (hierarchy.canStoreType(incumbentBase, newBase)) { j.remove(); changes++; } } else if (incumbentType instanceof AnySubType) { RefType incumbentBase = ((AnySubType) incumbentType).getBase(); if (newBase == incumbentBase) { // assertion failure. throw new IllegalStateException( "ThrowableSet.add(Set): resultSet.contains() failed to screen duplicate AnySubType " + newBase); } else if (hierarchy.canStoreType(incumbentBase, newBase)) { j.remove(); changes++; } else if (hierarchy.canStoreType(newBase, incumbentBase)) { // No need to add this class. addNewType = false; } } else { // assertion failure. throw new IllegalStateException( "ThrowableSet.add(Set): old Set element " + incumbentType + " is neither a RefType nor an AnySubType."); } } } else { // assertion failure. throw new IllegalArgumentException( "ThrowableSet.add(Set): new Set element " + newType + " is neither a RefType nor an AnySubType."); } if (addNewType) { changes++; resultSet.add(newType); } } } ThrowableSet result = null; if (changes > 0) { result = Manager.v().registerSetIfNew(resultSet, this.exceptionsExcluded); } else { result = this; } return result; } /** * Returns a ThrowableSet which contains all the exceptions in this ThrowableSet except for those * in removedExceptions. * * @param removedExceptions * a set of {@link RefLikeType} and {@link AnySubType} objects to be added to the types included in this * ThrowableSet. * * @return a set containing all the addedExceptions as well as the exceptions in this set. */ private ThrowableSet remove(Set removedExceptions) { // Is there anything to remove? if (removedExceptions.isEmpty()) { return this; } int changes = 0; Set resultSet = new HashSet<>(this.exceptionsIncluded); for (RefLikeType tp : removedExceptions) { if (tp instanceof RefType) { if (resultSet.remove(tp)) { changes++; } } } ThrowableSet result = null; if (changes > 0) { result = Manager.v().registerSetIfNew(resultSet, this.exceptionsExcluded); } else { result = this; } return result; } /** * Returns a ThrowableSet which contains all the exceptions from the current set except for those in the given * ThrowableSet. * * @param s * The set containing the exceptions to exclude from the new set * * @return The exceptions that are only in this set, but not in the given set * * @throws ThrowableSet.AlreadyHasExclusionsException * if this ThrowableSet or s is the result of a {@link #whichCatchableAs(RefType)} * operation, so that it is not possible to represent the addition of s to this * ThrowableSet. */ public ThrowableSet remove(ThrowableSet s) { if ((exceptionsExcluded.size() > 0) || (s.exceptionsExcluded.size() > 0)) { throw new AlreadyHasExclusionsException( "ThrowableSet.Add(ThrowableSet): attempt to add to [" + this.toString() + "] after removals recorded."); } // Remove the exceptions return this.remove(s.exceptionsIncluded); } /** * Indicates whether this ThrowableSet includes some exception that might be caught by a handler argument of the type * catcher. * * @param catcher * type of the handler parameter to be tested. * * @return true if this set contains an exception type that might be caught by catcher, false if * it does not. */ public boolean catchableAs(RefType catcher) { if (INSTRUMENTING) { Manager.v().catchableAsQueries++; } FastHierarchy h = Scene.v().getOrMakeFastHierarchy(); /** * Originally this implementation had checked if the catcher.getSootClass() is a phantom class. However this makes * problems in case the soot option no_bodies_for_excluded==true because certain library classes will be marked as * phantom classes even if they have a hierarchy. The workaround for this problem is to check for the suerClass. As every * class except java.lang.Object have a superClass (even interfaces have!) only real phantom classes can be identified * using this method. */ boolean catcherHasNoHierarchy = hasNoHierarchy(catcher); if (exceptionsExcluded.size() > 0) { if (INSTRUMENTING) { Manager.v().catchableAsFromSearch++; } for (AnySubType exclusion : exceptionsExcluded) { if (catcherHasNoHierarchy) { if (exclusion.getBase().equals(catcher)) { return false; } } else if (h.canStoreType(catcher, exclusion.getBase())) { return false; } } } if (exceptionsIncluded.contains(catcher)) { if (INSTRUMENTING) { if (exceptionsExcluded.size() == 0) { Manager.v().catchableAsFromMap++; } else { Manager.v().catchableAsFromSearch++; } } return true; } else { if (INSTRUMENTING) { if (exceptionsExcluded.size() == 0) { Manager.v().catchableAsFromSearch++; } } for (RefLikeType thrownType : exceptionsIncluded) { if (thrownType instanceof RefType) { if (thrownType == catcher) { // assertion failure. throw new IllegalStateException( "ThrowableSet.catchableAs(RefType): exceptions.contains() failed to match contained RefType " + catcher); } else if (!catcherHasNoHierarchy && h.canStoreType(thrownType, catcher)) { return true; } } else { RefType thrownBase = ((AnySubType) thrownType).getBase(); if (catcherHasNoHierarchy) { if (thrownBase.equals(catcher) || thrownBase.getClassName().equals("java.lang.Throwable")) { return true; } } // At runtime, thrownType might be instantiated by any // of thrownBase's subtypes, so: else if (h.canStoreType(thrownBase, catcher) || h.canStoreType(catcher, thrownBase)) { return true; } } } return false; } } /** * Partitions the exceptions in this ThrowableSet into those which would be caught by a handler with the * passed catch parameter type and those which would not. * * @param catcher * type of the handler parameter to be tested. * * @return a pair of ThrowableSets, one containing the types in this ThrowableSet which would be * be caught as catcher and the other containing the types in this ThrowableSet which * would not be caught as catcher. */ public Pair whichCatchableAs(RefType catcher) { if (INSTRUMENTING) { Manager.v().removesOfAnySubType++; } FastHierarchy h = Scene.v().getOrMakeFastHierarchy(); Set caughtIncluded = null; Set caughtExcluded = null; Set uncaughtIncluded = null; Set uncaughtExcluded = null; if (INSTRUMENTING) { Manager.v().removesFromSearch++; } boolean catcherHasNoHierarchy = hasNoHierarchy(catcher); for (AnySubType exclusion : exceptionsExcluded) { RefType exclusionBase = exclusion.getBase(); // Is the current type explicitly excluded? if (catcherHasNoHierarchy && exclusionBase.equals(catcher)) { return new Pair(ThrowableSet.Manager.v().EMPTY, this); } if (h.canStoreType(catcher, exclusionBase)) { // Because the add() operations ban additions to sets // with exclusions, we can be sure no types in this are // caught by catcher. return new Pair(ThrowableSet.Manager.v().EMPTY, this); } else if (h.canStoreType(exclusionBase, catcher)) { // exclusion wouldn't be in exceptionsExcluded if one // of its supertypes were not in exceptionsIncluded, // so we know the next loop will add either that supertype // or catcher to caughtIncluded. Thus: caughtExcluded = addExceptionToSet(exclusion, caughtExcluded); } else { uncaughtExcluded = addExceptionToSet(exclusion, uncaughtExcluded); } } for (RefLikeType inclusion : exceptionsIncluded) { if (inclusion instanceof RefType) { // If the current type is has no hierarchy, we catch it if and // only if it is in the inclusion list and ignore any hierarchy. if (catcherHasNoHierarchy) { if (inclusion.equals(catcher)) { caughtIncluded = addExceptionToSet(inclusion, caughtIncluded); } else { uncaughtIncluded = addExceptionToSet(inclusion, uncaughtIncluded); } } else if (h.canStoreType(inclusion, catcher)) { caughtIncluded = addExceptionToSet(inclusion, caughtIncluded); } else { uncaughtIncluded = addExceptionToSet(inclusion, uncaughtIncluded); } } else { RefType base = ((AnySubType) inclusion).getBase(); // If the current type is has no hierarchy, we catch it if and // only if it is in the inclusion list and ignore any hierarchy. if (catcherHasNoHierarchy) { if (base.equals(catcher)) { caughtIncluded = addExceptionToSet(inclusion, caughtIncluded); } else { if (base.getClassName().equals("java.lang.Throwable")) { caughtIncluded = addExceptionToSet(catcher, caughtIncluded); } uncaughtIncluded = addExceptionToSet(inclusion, uncaughtIncluded); } } else if (h.canStoreType(base, catcher)) { // All subtypes of base will be caught. Any exclusions // will already have been copied to caughtExcluded by // the preceding loop. caughtIncluded = addExceptionToSet(inclusion, caughtIncluded); } else if (h.canStoreType(catcher, base)) { // Some subtypes of base will be caught, and // we know that not all of those catchable subtypes // are among exceptionsExcluded, since in that case we // would already have returned from within the // preceding loop. So, remove AnySubType(catcher) // from the uncaught types. uncaughtIncluded = addExceptionToSet(inclusion, uncaughtIncluded); uncaughtExcluded = addExceptionToSet(AnySubType.v(catcher), uncaughtExcluded); caughtIncluded = addExceptionToSet(AnySubType.v(catcher), caughtIncluded); // Any already excluded subtypes of inclusion // which are subtypes of catcher will have been // added to caughtExcluded by the previous loop. } else { uncaughtIncluded = addExceptionToSet(inclusion, uncaughtIncluded); } } } ThrowableSet caughtSet = Manager.v().registerSetIfNew(caughtIncluded, caughtExcluded); ThrowableSet uncaughtSet = Manager.v().registerSetIfNew(uncaughtIncluded, uncaughtExcluded); return new Pair(caughtSet, uncaughtSet); } /** * Utility method for building sets of exceptional types for a {@link Pair}. * * @param e * The exceptional type to add to the set. * * @param set * The Set to which to add the types, or null if no Set has yet been * allocated. * * @return A Set containing the elements in set plus e. */ private Set addExceptionToSet(T e, Set set) { if (set == null) { set = new HashSet<>(); } set.add(e); return set; } /** * Returns a string representation of this ThrowableSet. */ @Override public String toString() { StringBuffer buffer = new StringBuffer(this.toBriefString()); buffer.append(":\n "); for (RefLikeType ei : exceptionsIncluded) { buffer.append('+'); buffer.append(ei == null ? "null" : ei.toString()); // buffer.append(i.next().toString()); } for (RefLikeType ee : exceptionsExcluded) { buffer.append('-'); buffer.append(ee.toString()); } return buffer.toString(); } /** * Returns a cryptic identifier for this ThrowableSet, used to identify a set when it appears in a collection. */ public String toBriefString() { return super.toString(); } /** *

* Produce an abbreviated representation of this ThrowableSet, suitable for human consumption. The * abbreviations include: *

* *
    * *
  • The strings “java.lang.” is stripped from the beginning of exception names.
  • * *
  • The string “Exception” is stripped from the ends of exception names.
  • * *
  • Instances of AnySubType are indicated by surrounding the base type name with parentheses, rather than * with the string “ Any_subtype_of_
  • * *
  • If this ThrowableSet includes all the elements of {@link ThrowableSet.Manager#VM_ERRORS VM_ERRORS}, * they are abbreviated as “vmErrors” rather than listed individually.
  • * * @return An abbreviated representation of the contents of this set. */ public String toAbbreviatedString() { return toAbbreviatedString(exceptionsIncluded, '+') + toAbbreviatedString(exceptionsExcluded, '-'); } /** *

    * Utility method which prints the abbreviations of the elements in a passed {@link Set} of exception types. *

    * * @param s * The exceptions to print. * * @param connector * The character to insert between exceptions. * * @return An abbreviated representation of the exceptions. */ private String toAbbreviatedString(Set s, char connector) { final String JAVA_LANG = "java.lang."; final String EXCEPTION = "Exception"; Collection vmErrorThrowables = ThrowableSet.Manager.v().VM_ERRORS.exceptionsIncluded; boolean containsAllVmErrors = s.containsAll(vmErrorThrowables); StringBuffer buf = new StringBuffer(); if (containsAllVmErrors) { buf.append(connector); buf.append("vmErrors"); } for (Iterator it = sortedThrowableIterator(s); it.hasNext();) { RefLikeType reflikeType = it.next(); RefType baseType = null; if (reflikeType instanceof RefType) { baseType = (RefType) reflikeType; if (containsAllVmErrors && vmErrorThrowables.contains(baseType)) { continue; // Already accounted for vmErrors. } else { buf.append(connector); } } else if (reflikeType instanceof AnySubType) { buf.append(connector); buf.append('('); baseType = ((AnySubType) reflikeType).getBase(); } else { throw new RuntimeException("Unsupported type " + reflikeType.getClass().getName()); } String typeName = baseType.toString(); int start = 0; int end = typeName.length(); if (typeName.startsWith(JAVA_LANG)) { start += JAVA_LANG.length(); } if (typeName.endsWith(EXCEPTION)) { end -= EXCEPTION.length(); } buf.append(typeName, start, end); if (reflikeType instanceof AnySubType) { buf.append(')'); } } return buf.toString(); } /** * A package-private method to provide unit tests with access to the {@link RefLikeType} objects which represent the * Throwable types included in this set. * * @return an unmodifiable collection view of the Throwable types in this set. */ Collection typesIncluded() { return exceptionsIncluded; } /** * A package-private method to provide unit tests with access to the {@link RefLikeType} objects which represent the * Throwable types excluded from this set. * * @return an unmodifiable collection view of the Throwable types excluded from this set. */ Collection typesExcluded() { return exceptionsExcluded; } /** * A package-private method to provide unit tests with access to ThrowableSet's internals. */ Map getMemoizedAdds() { if (memoizedAdds == null) { return Collections.emptyMap(); } else { return Collections.unmodifiableMap(memoizedAdds); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + exceptionsIncluded.hashCode(); result = (prime * result) + exceptionsExcluded.hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ThrowableSet other = (ThrowableSet) obj; return exceptionsIncluded.equals(other.exceptionsIncluded) && exceptionsExcluded.equals(other.exceptionsExcluded); } /** * Singleton class for fields and initializers common to all ThrowableSet objects (i.e., these would be static fields and * initializers, in the absence of soot's {@link G} and {@link Singletons} classes). */ public static class Manager { /** * ThrowableSet containing no exception classes. */ public final ThrowableSet EMPTY; /** * ThrowableSet containing all the exceptions that may be thrown in the course of resolving a reference to * another class, including the process of loading, preparing, and verifying the referenced class. */ public final ThrowableSet RESOLVE_CLASS_ERRORS; public final RefType RUNTIME_EXCEPTION; public final RefType ARITHMETIC_EXCEPTION; public final RefType ARRAY_STORE_EXCEPTION; public final RefType CLASS_CAST_EXCEPTION; public final RefType ILLEGAL_MONITOR_STATE_EXCEPTION; public final RefType INDEX_OUT_OF_BOUNDS_EXCEPTION; public final RefType ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION; public final RefType NEGATIVE_ARRAY_SIZE_EXCEPTION; public final RefType NULL_POINTER_EXCEPTION; public final RefType INSTANTIATION_ERROR; /** * ThrowableSet representing all possible Throwables. */ final ThrowableSet ALL_THROWABLES; /** * ThrowableSet containing all the asynchronous and virtual machine errors, which may be thrown by any * bytecode instruction at any point in the computation. */ final ThrowableSet VM_ERRORS; /** * ThrowableSet containing all the exceptions that may be thrown in the course of resolving a reference to a * field. */ final ThrowableSet RESOLVE_FIELD_ERRORS; /** * ThrowableSet containing all the exceptions that may be thrown in the course of resolving a reference to a * non-static method. */ final ThrowableSet RESOLVE_METHOD_ERRORS; /** * ThrowableSet containing all the exceptions which may be thrown by instructions that have the potential to * cause a new class to be loaded and initialized (including UnsatisfiedLinkError, which is raised at runtime rather than * linking type). */ final ThrowableSet INITIALIZATION_ERRORS; /** * This map stores all referenced ThrowableSets. */ private final Map registry = CacheBuilder.newBuilder().weakValues().build().asMap(); private final int removesFromMap = 0; private final int removesFromMemo = 0; // counts for instrumenting: private int addsOfRefType = 0; private int addsOfAnySubType = 0; private int addsOfSet = 0; private int addsInclusionFromMap = 0; private int addsInclusionFromMemo = 0; private int addsInclusionFromSearch = 0; private int addsInclusionInterrupted = 0; private int addsExclusionWithSearch = 0; private int addsExclusionWithoutSearch = 0; private int removesOfAnySubType = 0; private int removesFromSearch = 0; private int registrationCalls = 0; private int catchableAsQueries = 0; private int catchableAsFromMap = 0; private int catchableAsFromSearch = 0; /** * Constructs a ThrowableSet.Manager for inclusion in Soot's global variable manager, {@link G}. * * @param g * guarantees that the constructor may only be called from {@link Singletons}. */ public Manager(Singletons.Global g) { // First ensure the Exception classes are represented in Soot. // Runtime errors: RUNTIME_EXCEPTION = Scene.v().getRefType("java.lang.RuntimeException"); ARITHMETIC_EXCEPTION = Scene.v().getRefType("java.lang.ArithmeticException"); ARRAY_STORE_EXCEPTION = Scene.v().getRefType("java.lang.ArrayStoreException"); CLASS_CAST_EXCEPTION = Scene.v().getRefType("java.lang.ClassCastException"); ILLEGAL_MONITOR_STATE_EXCEPTION = Scene.v().getRefType("java.lang.IllegalMonitorStateException"); INDEX_OUT_OF_BOUNDS_EXCEPTION = Scene.v().getRefType("java.lang.IndexOutOfBoundsException"); ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = Scene.v().getRefType("java.lang.ArrayIndexOutOfBoundsException"); NEGATIVE_ARRAY_SIZE_EXCEPTION = Scene.v().getRefType("java.lang.NegativeArraySizeException"); NULL_POINTER_EXCEPTION = Scene.v().getRefType("java.lang.NullPointerException"); INSTANTIATION_ERROR = Scene.v().getRefType("java.lang.InstantiationError"); EMPTY = registerSetIfNew(null, null); Set allThrowablesSet = new HashSet<>(); allThrowablesSet.add(AnySubType.v(Scene.v().getRefType("java.lang.Throwable"))); ALL_THROWABLES = registerSetIfNew(allThrowablesSet, null); Set vmErrorSet = new HashSet<>(); vmErrorSet.add(Scene.v().getRefType("java.lang.InternalError")); vmErrorSet.add(Scene.v().getRefType("java.lang.OutOfMemoryError")); vmErrorSet.add(Scene.v().getRefType("java.lang.StackOverflowError")); vmErrorSet.add(Scene.v().getRefType("java.lang.UnknownError")); // The Java library's deprecated Thread.stop(Throwable) method // would actually allow _any_ Throwable to be delivered // asynchronously, not just java.lang.ThreadDeath. vmErrorSet.add(Scene.v().getRefType("java.lang.ThreadDeath")); VM_ERRORS = registerSetIfNew(vmErrorSet, null); Set resolveClassErrorSet = new HashSet<>(); resolveClassErrorSet.add(Scene.v().getRefType("java.lang.ClassCircularityError")); // We add AnySubType(ClassFormatError) so that we can // avoid adding its subclass, // UnsupportedClassVersionError, explicitly. This is a // hack to allow Soot to analyze older class libraries // (UnsupportedClassVersionError was added in JDK 1.2). if (!Options.v().j2me()) { resolveClassErrorSet.add(AnySubType.v(Scene.v().getRefType("java.lang.ClassFormatError"))); } resolveClassErrorSet.add(Scene.v().getRefType("java.lang.IllegalAccessError")); resolveClassErrorSet.add(Scene.v().getRefType("java.lang.IncompatibleClassChangeError")); resolveClassErrorSet.add(Scene.v().getRefType("java.lang.LinkageError")); resolveClassErrorSet.add(Scene.v().getRefType("java.lang.NoClassDefFoundError")); resolveClassErrorSet.add(Scene.v().getRefType("java.lang.VerifyError")); RESOLVE_CLASS_ERRORS = registerSetIfNew(resolveClassErrorSet, null); Set resolveFieldErrorSet = new HashSet<>(resolveClassErrorSet); resolveFieldErrorSet.add(Scene.v().getRefType("java.lang.NoSuchFieldError")); RESOLVE_FIELD_ERRORS = registerSetIfNew(resolveFieldErrorSet, null); Set resolveMethodErrorSet = new HashSet<>(resolveClassErrorSet); resolveMethodErrorSet.add(Scene.v().getRefType("java.lang.AbstractMethodError")); resolveMethodErrorSet.add(Scene.v().getRefType("java.lang.NoSuchMethodError")); resolveMethodErrorSet.add(Scene.v().getRefType("java.lang.UnsatisfiedLinkError")); RESOLVE_METHOD_ERRORS = registerSetIfNew(resolveMethodErrorSet, null); // The static initializers of a newly loaded class might // throw any Error (if they threw an Exception---even a // RuntimeException---it would be replaced by an // ExceptionInInitializerError): // Set initializationErrorSet = new HashSet<>(); initializationErrorSet.add(AnySubType.v(Scene.v().getRefType("java.lang.Error"))); INITIALIZATION_ERRORS = registerSetIfNew(initializationErrorSet, null); } /** * Returns the single instance of ThrowableSet.Manager. * * @return Soot's ThrowableSet.Manager. */ public static Manager v() { return G.v().soot_toolkits_exceptions_ThrowableSet_Manager(); } /** *

    * Returns a ThrowableSet representing the set of exceptions included in include minus the set * of exceptions included in exclude. Creates a new ThrowableSet only if there was not already * one whose contents correspond to include - exclude. *

    * * @param include * A set of {@link RefLikeType} objects representing exception types included in the result; may be * null if there are no included types. * * @param exclude * A set of {@link AnySubType} objects representing exception types excluded from the result; may be * null if there are no excluded types. * * @return a ThrowableSet representing the set of exceptions corresponding to include - * exclude. */ private ThrowableSet registerSetIfNew(Set include, Set exclude) { if (INSTRUMENTING) { registrationCalls++; } ThrowableSet result = new ThrowableSet(include, exclude); ThrowableSet ref = registry.get(result); if (null != ref) { return ref; } registry.put(result, result); return result; } /** * Report the counts collected by instrumentation (for now, at least, there is no need to provide access to the * individual values as numbers). * * @return a string listing the counts. */ public String reportInstrumentation() { int setCount = registry.size(); StringBuffer buf = new StringBuffer("registeredSets: ").append(setCount).append("\naddsOfRefType: ") .append(addsOfRefType).append("\naddsOfAnySubType: ").append(addsOfAnySubType).append("\naddsOfSet: ") .append(addsOfSet).append("\naddsInclusionFromMap: ").append(addsInclusionFromMap) .append("\naddsInclusionFromMemo: ").append(addsInclusionFromMemo).append("\naddsInclusionFromSearch: ") .append(addsInclusionFromSearch).append("\naddsInclusionInterrupted: ").append(addsInclusionInterrupted) .append("\naddsExclusionWithoutSearch: ").append(addsExclusionWithoutSearch).append("\naddsExclusionWithSearch: ") .append(addsExclusionWithSearch).append("\nremovesOfAnySubType: ").append(removesOfAnySubType) .append("\nremovesFromMap: ").append(removesFromMap).append("\nremovesFromMemo: ").append(removesFromMemo) .append("\nremovesFromSearch: ").append(removesFromSearch).append("\nregistrationCalls: ") .append(registrationCalls).append("\ncatchableAsQueries: ").append(catchableAsQueries) .append("\ncatchableAsFromMap: ").append(catchableAsFromMap).append("\ncatchableAsFromSearch: ") .append(catchableAsFromSearch).append('\n'); return buf.toString(); } /** * A package-private method to provide unit tests with access to the collection of ThrowableSets. */ Set getThrowableSets() { return registry.keySet(); } } public static class AlreadyHasExclusionsException extends IllegalStateException { private static final long serialVersionUID = 6785184160868722359L; public AlreadyHasExclusionsException(String s) { super(s); } } /** * The return type for {@link ThrowableSet#whichCatchableAs(RefType)}, consisting of a pair of ThrowableSets. */ public static class Pair { private ThrowableSet caught; private ThrowableSet uncaught; /** * Constructs a ThrowableSet.Pair. * * @param caught * The set of exceptions to be returned when {@link #getCaught()} is called on the constructed * ThrowableSet.Pair. * * @param uncaught * The set of exceptions to be returned when {@link #getUncaught()} is called on the constructed * ThrowableSet.Pair. */ protected Pair(ThrowableSet caught, ThrowableSet uncaught) { this.caught = caught; this.uncaught = uncaught; } /** * @return the set of caught exceptions. */ public ThrowableSet getCaught() { return caught; } /** * @return the set of uncaught exceptions. */ public ThrowableSet getUncaught() { return uncaught; } /** * Indicates whether two {@link Object}s are ThrowableSet.Pairs representing the same set of caught and * uncaught exception types. * * @param o * the Object to compare to this ThrowableSet.Pair. * * @return true if o is a ThrowableSet.Pair representing the same set of caught * and uncaught types as this ThrowableSet.Pair. */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Pair)) { return false; } Pair tsp = (Pair) o; if (this.caught.equals(tsp.caught) && this.uncaught.equals(tsp.uncaught)) { return true; } return false; } @Override public int hashCode() { int result = 31; result = (37 * result) + caught.hashCode(); result = (37 * result) + uncaught.hashCode(); return result; } } /** * Comparator used to implement sortedThrowableIterator(). * */ private static class ThrowableComparator implements java.util.Comparator { private static RefType baseType(RefLikeType o) { if (o instanceof AnySubType) { return ((AnySubType) o).getBase(); } else { return (RefType) o; // ClassCastException if o is not a RefType. } } @Override public int compare(T o1, T o2) { RefType t1 = baseType(o1); RefType t2 = baseType(o2); if (t1.equals(t2)) { // There should never be both AnySubType(t) and // t in a ThrowableSet, but if it happens, put // AnySubType(t) first: if (o1 instanceof AnySubType) { if (o2 instanceof AnySubType) { return 0; } else { return -1; } } else if (o2 instanceof AnySubType) { return 1; } else { return 0; } } else { return t1.toString().compareTo(t2.toString()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy