Exemplo n.º 1
0
  public void visitExceptionInfo(
      Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) {
    int startPC = Math.max(exceptionInfo.u2startPC, clipStart);
    int endPC = Math.min(exceptionInfo.u2endPC, clipEnd);
    int handlerPC = exceptionInfo.u2handlerPC;
    int catchType = exceptionInfo.u2catchType;

    // Exclude any subroutine invocations that jump out of the try block,
    // by adding a try block before (and later on, after) each invocation.
    for (int offset = startPC; offset < endPC; offset++) {
      if (branchTargetFinder.isSubroutineInvocation(offset)) {
        Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
        int instructionLength = instruction.length(offset);

        // Is it a subroutine invocation?
        if (!exceptionInfo.isApplicable(offset + ((BranchInstruction) instruction).branchOffset)) {
          if (DEBUG) {
            System.out.println(
                "  Appending extra exception [" + startPC + " -> " + offset + "] -> " + handlerPC);
          }

          // Append a try block that ends before the subroutine invocation.
          codeAttributeComposer.appendException(
              new ExceptionInfo(startPC, offset, handlerPC, catchType));

          // The next try block will start after the subroutine invocation.
          startPC = offset + instructionLength;
        }
      }
    }

    if (DEBUG) {
      System.out.println(
          "  Appending exception [" + startPC + " -> " + endPC + "] -> " + handlerPC);
    }

    // Append the exception. Note that exceptions with empty try blocks
    // are automatically ignored.
    codeAttributeComposer.appendException(new ExceptionInfo(startPC, endPC, handlerPC, catchType));
  }
  public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
    // Update the descriptor if it has any unused parameters.
    String descriptor = programMethod.getDescriptor(programClass);
    String newDescriptor = shrinkDescriptor(programMethod, descriptor);

    if (!descriptor.equals(newDescriptor)) {
      // Shrink the signature and parameter annotations.
      programMethod.attributesAccept(programClass, this);

      String name = programMethod.getName(programClass);
      String newName = name;

      // Append a code, if the method isn't a class instance initializer.
      if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) {
        newName +=
            ClassConstants.SPECIAL_MEMBER_SEPARATOR
                + Long.toHexString(Math.abs((descriptor).hashCode()));
      }

      if (DEBUG) {
        System.out.println("MethodDescriptorShrinker:");
        System.out.println("  Class file        = " + programClass.getName());
        System.out.println("  Method name       = " + name);
        System.out.println("                   -> " + newName);
        System.out.println("  Method descriptor = " + descriptor);
        System.out.println("                   -> " + newDescriptor);
      }

      ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(programClass);

      // Update the name, if necessary.
      if (!newName.equals(name)) {
        programMethod.u2nameIndex = constantPoolEditor.addUtf8Constant(newName);
      }

      // Update the referenced classes.
      programMethod.referencedClasses =
          shrinkReferencedClasses(programMethod, descriptor, programMethod.referencedClasses);

      // Finally, update the descriptor itself.
      programMethod.u2descriptorIndex = constantPoolEditor.addUtf8Constant(newDescriptor);

      // Visit the method, if required.
      if (extraMemberVisitor != null) {
        extraMemberVisitor.visitProgramMethod(programClass, programMethod);
      }
    }
  }
Exemplo n.º 3
0
  public int instanceOf(String otherType, Clazz otherReferencedClass) {
    String thisType = this.type;

    // If this type is null, it is never an instance of any class.
    if (thisType == null) {
      return NEVER;
    }

    // Start taking into account the type dimensions.
    int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
    int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
    int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);

    // Strip any common array prefixes.
    thisType = thisType.substring(commonDimensionCount);
    otherType = otherType.substring(commonDimensionCount);

    // If either stripped type is a primitive type, we can tell right away.
    if (commonDimensionCount > 0
        && (ClassUtil.isInternalPrimitiveType(thisType.charAt(0))
            || ClassUtil.isInternalPrimitiveType(otherType.charAt(0)))) {
      return !thisType.equals(otherType) ? NEVER : mayBeNull ? MAYBE : ALWAYS;
    }

    // Strip the class type prefix and suffix of this type, if any.
    if (thisDimensionCount == commonDimensionCount) {
      thisType = ClassUtil.internalClassNameFromClassType(thisType);
    }

    // Strip the class type prefix and suffix of the other type, if any.
    if (otherDimensionCount == commonDimensionCount) {
      otherType = ClassUtil.internalClassNameFromClassType(otherType);
    }

    // If this type is an array type, and the other type is not
    // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
    // this type can never be an instance.
    if (thisDimensionCount > otherDimensionCount
        && !ClassUtil.isInternalArrayInterfaceName(otherType)) {
      return NEVER;
    }

    // If the other type is an array type, and this type is not
    // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
    // this type can never be an instance.
    if (thisDimensionCount < otherDimensionCount
        && !ClassUtil.isInternalArrayInterfaceName(thisType)) {
      return NEVER;
    }

    // If this type may be null, it might not be an instance of any class.
    if (mayBeNull) {
      return MAYBE;
    }

    // If this type is equal to the other type, or if the other type is
    // java.lang.Object, this type is always an instance.
    if (thisType.equals(otherType) || ClassConstants.NAME_JAVA_LANG_OBJECT.equals(otherType)) {
      return ALWAYS;
    }

    // If this type is an array type, it's ok.
    if (thisDimensionCount > otherDimensionCount) {
      return ALWAYS;
    }

    // If the other type is an array type, it might be ok.
    if (thisDimensionCount < otherDimensionCount) {
      return MAYBE;
    }

    // If the value extends the type, we're sure.
    return referencedClass != null
            && otherReferencedClass != null
            && referencedClass.extendsOrImplements(otherReferencedClass)
        ? ALWAYS
        : MAYBE;
  }
Exemplo n.º 4
0
  public ReferenceValue generalize(TypedReferenceValue other) {
    // If both types are identical, the generalization is the same too.
    if (this.equals(other)) {
      return this;
    }

    String thisType = this.type;
    String otherType = other.type;

    // If both types are nul, the generalization is null too.
    if (thisType == null && otherType == null) {
      return ValueFactory.REFERENCE_VALUE_NULL;
    }

    // If this type is null, the generalization is the other type, maybe null.
    if (thisType == null) {
      return other.generalizeMayBeNull(true);
    }

    // If the other type is null, the generalization is this type, maybe null.
    if (otherType == null) {
      return this.generalizeMayBeNull(true);
    }

    boolean mayBeNull = this.mayBeNull || other.mayBeNull;

    // If the two types are equal, the generalization remains the same, maybe null.
    if (thisType.equals(otherType)) {
      return typedReferenceValue(this, mayBeNull);
    }

    // Start taking into account the type dimensions.
    int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
    int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
    int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);

    if (thisDimensionCount == otherDimensionCount) {
      // See if we can take into account the referenced classes.
      Clazz thisReferencedClass = this.referencedClass;
      Clazz otherReferencedClass = other.referencedClass;

      if (thisReferencedClass != null && otherReferencedClass != null) {
        // Is one class simply an extension of the other one?
        if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) {
          return typedReferenceValue(other, mayBeNull);
        }

        if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) {
          return typedReferenceValue(this, mayBeNull);
        }

        // Do the classes have a non-trivial common superclass?
        Clazz commonClass = findCommonClass(thisReferencedClass, otherReferencedClass, false);

        if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) {
          // Otherwise, do the classes have a common interface?
          Clazz commonInterface = findCommonClass(thisReferencedClass, otherReferencedClass, true);
          if (commonInterface != null) {
            commonClass = commonInterface;
          }
        }

        return new TypedReferenceValue(
            commonDimensionCount == 0
                ? commonClass.getName()
                : ClassUtil.internalArrayTypeFromClassName(
                    commonClass.getName(), commonDimensionCount),
            commonClass,
            mayBeNull);
      }
    } else if (thisDimensionCount > otherDimensionCount) {
      // See if the other type is an interface type of arrays.
      if (ClassUtil.isInternalArrayInterfaceName(
          ClassUtil.internalClassNameFromClassType(otherType))) {
        return typedReferenceValue(other, mayBeNull);
      }
    } else if (thisDimensionCount < otherDimensionCount) {
      // See if this type is an interface type of arrays.
      if (ClassUtil.isInternalArrayInterfaceName(
          ClassUtil.internalClassNameFromClassType(thisType))) {
        return typedReferenceValue(this, mayBeNull);
      }
    }

    // Reduce the common dimension count if either type is an array of
    // primitives type of this dimension.
    if (commonDimensionCount > 0
            && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount)))
        || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) {
      commonDimensionCount--;
    }

    // Fall back on a basic Object or array of Objects type.
    return commonDimensionCount != 0
        ? new TypedReferenceValue(
            ClassUtil.internalArrayTypeFromClassName(
                ClassConstants.NAME_JAVA_LANG_OBJECT, commonDimensionCount),
            null,
            mayBeNull)
        : mayBeNull
            ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL
            : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL;
  }
  /** Returns the generalization of this ReferenceValue and the given other ReferenceValue. */
  public ReferenceValue generalize(ReferenceValue other) {
    // If both types are identical, the generalization is the same too.
    if (this.equals(other)) {
      return this;
    }

    String thisType = this.type;
    String otherType = other.type;

    // If both types are nul, the generalization is null too.
    if (thisType == null && otherType == null) {
      return ValueFactory.REFERENCE_VALUE_NULL;
    }

    // If this type is null, the generalization is the other type, maybe null.
    if (thisType == null) {
      return other.generalizeMayBeNull(true);
    }

    // If the other type is null, the generalization is this type, maybe null.
    if (otherType == null) {
      return this.generalizeMayBeNull(true);
    }

    boolean mayBeNull = this.mayBeNull || other.mayBeNull;

    // If the two types are equal, the generalization remains the same, maybe null.
    if (thisType.equals(otherType)) {
      return this.generalizeMayBeNull(mayBeNull);
    }

    // Start taking into account the type dimensions.
    int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
    int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
    int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);

    if (thisDimensionCount == otherDimensionCount) {
      // See if we can take into account the referenced classes.
      Clazz thisReferencedClass = this.referencedClass;
      Clazz otherReferencedClass = other.referencedClass;

      if (thisReferencedClass != null && otherReferencedClass != null) {
        if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) {
          return other.generalizeMayBeNull(mayBeNull);
        }

        if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) {
          return this.generalizeMayBeNull(mayBeNull);
        }

        // Collect the superclasses and interfaces of this class.
        Set thisSuperClasses = new HashSet();
        thisReferencedClass.hierarchyAccept(
            false, true, true, false, new ClassCollector(thisSuperClasses));

        int thisSuperClassesCount = thisSuperClasses.size();
        if (thisSuperClassesCount == 0 && thisReferencedClass.getSuperName() != null) {
          throw new IllegalArgumentException(
              "Can't find any super classes of ["
                  + thisType
                  + "] (not even immediate super class ["
                  + thisReferencedClass.getSuperName()
                  + "])");
        }

        // Collect the superclasses and interfaces of the other class.
        Set otherSuperClasses = new HashSet();
        otherReferencedClass.hierarchyAccept(
            false, true, true, false, new ClassCollector(otherSuperClasses));

        int otherSuperClassesCount = otherSuperClasses.size();
        if (otherSuperClassesCount == 0 && otherReferencedClass.getSuperName() != null) {
          throw new IllegalArgumentException(
              "Can't find any super classes of ["
                  + otherType
                  + "] (not even immediate super class ["
                  + otherReferencedClass.getSuperName()
                  + "])");
        }

        if (DEBUG) {
          System.out.println(
              "ReferenceValue.generalize this ["
                  + thisReferencedClass.getName()
                  + "] with other ["
                  + otherReferencedClass.getName()
                  + "]");
          System.out.println("  This super classes:  " + thisSuperClasses);
          System.out.println("  Other super classes: " + otherSuperClasses);
        }

        // Find the common superclasses.
        thisSuperClasses.retainAll(otherSuperClasses);

        if (DEBUG) {
          System.out.println("  Common super classes: " + thisSuperClasses);
        }

        // Find a class that is a subclass of all common superclasses,
        // or that at least has the maximum number of common superclasses.
        Clazz commonClass = null;

        int maximumSuperClassCount = -1;

        // Go over all common superclasses to find it. In case of
        // multiple subclasses, keep the lowest one alphabetically,
        // in order to ensure that the choice is deterministic.
        Iterator commonSuperClasses = thisSuperClasses.iterator();
        while (commonSuperClasses.hasNext()) {
          Clazz commonSuperClass = (Clazz) commonSuperClasses.next();

          int superClassCount = superClassCount(commonSuperClass, thisSuperClasses);
          if (maximumSuperClassCount < superClassCount
              || (maximumSuperClassCount == superClassCount
                  && commonClass != null
                  && commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) {
            commonClass = commonSuperClass;
            maximumSuperClassCount = superClassCount;
          }
        }

        if (commonClass == null) {
          throw new IllegalArgumentException(
              "Can't find common super class of ["
                  + thisType
                  + "] (with "
                  + thisSuperClassesCount
                  + " known super classes) and ["
                  + otherType
                  + "] (with "
                  + otherSuperClassesCount
                  + " known super classes)");
        }

        if (DEBUG) {
          System.out.println("  Best common class: [" + commonClass.getName() + "]");
        }

        // TODO: Handle more difficult cases, with multiple global subclasses.

        return new ReferenceValue(
            commonDimensionCount == 0
                ? commonClass.getName()
                : ClassUtil.internalArrayTypeFromClassName(
                    commonClass.getName(), commonDimensionCount),
            commonClass,
            mayBeNull);
      }
    } else if (thisDimensionCount > otherDimensionCount) {
      // See if the other type is an interface type of arrays.
      if (ClassUtil.isInternalArrayInterfaceName(
          ClassUtil.internalClassNameFromClassType(otherType))) {
        return other.generalizeMayBeNull(mayBeNull);
      }
    } else if (thisDimensionCount < otherDimensionCount) {
      // See if this type is an interface type of arrays.
      if (ClassUtil.isInternalArrayInterfaceName(
          ClassUtil.internalClassNameFromClassType(thisType))) {
        return this.generalizeMayBeNull(mayBeNull);
      }
    }

    // Reduce the common dimension count if either type is an array of
    // primitives type of this dimension.
    if (commonDimensionCount > 0
            && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount)))
        || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) {
      commonDimensionCount--;
    }

    // Fall back on a basic Object or array of Objects type.
    return commonDimensionCount == 0
        ? mayBeNull
            ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL
            : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL
        : new ReferenceValue(
            ClassUtil.internalArrayTypeFromClassName(
                ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, commonDimensionCount),
            null,
            mayBeNull);
  }