Exemple #1
0
  public void visitLibraryClass(LibraryClass libraryClass) {
    if (shouldBeMarkedAsUsed(libraryClass)) {
      markAsUsed(libraryClass);

      // We're not going to analyze all library code. We're assuming that
      // if this class is being used, all of its methods will be used as
      // well. We'll mark them as such (here and in all subclasses).

      // Mark the superclass.
      Clazz superClass = libraryClass.superClass;
      if (superClass != null) {
        superClass.accept(this);
      }

      // Mark the interfaces.
      Clazz[] interfaceClasses = libraryClass.interfaceClasses;
      if (interfaceClasses != null) {
        for (int index = 0; index < interfaceClasses.length; index++) {
          if (interfaceClasses[index] != null) {
            interfaceClasses[index].accept(this);
          }
        }
      }

      // Mark all methods.
      libraryClass.methodsAccept(this);
    }
  }
  /**
   * Writes the given word to the given writer, after having adapted it, based on the renamed class
   * names.
   */
  private void writeUpdatedWord(Writer writer, String word) throws IOException {
    if (word.length() > 0) {
      String newWord = word;

      boolean containsDots = word.indexOf('.') >= 0;

      // Replace dots by forward slashes.
      String className =
          containsDots ? word.replace('.', ClassConstants.INTERNAL_PACKAGE_SEPARATOR) : word;

      // Find the class corrsponding to the word.
      Clazz clazz = classPool.getClass(className);
      if (clazz != null) {
        // Update the word if necessary.
        String newClassName = clazz.getName();
        if (!className.equals(newClassName)) {
          // Replace forward slashes by dots.
          newWord =
              containsDots
                  ? newClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, '.')
                  : newClassName;
        }
      }

      writer.write(newWord);
    }
  }
  public int stackPopCount(Clazz clazz) {
    int stackPopCount = super.stackPopCount(clazz);

    // Some special cases.
    switch (opcode) {
      case InstructionConstants.OP_MULTIANEWARRAY:
        // For each dimension, an integer size is popped from the stack.
        stackPopCount += constant;
        break;

      case InstructionConstants.OP_PUTSTATIC:
      case InstructionConstants.OP_PUTFIELD:
        // The field value is be popped from the stack.
        clazz.constantPoolEntryAccept(constantIndex, this);
        stackPopCount += typeStackDelta;
        break;

      case InstructionConstants.OP_INVOKEVIRTUAL:
      case InstructionConstants.OP_INVOKESPECIAL:
      case InstructionConstants.OP_INVOKESTATIC:
      case InstructionConstants.OP_INVOKEINTERFACE:
        // The some parameters may be popped from the stack.
        clazz.constantPoolEntryAccept(constantIndex, this);
        stackPopCount += parameterStackDelta;
        break;
    }

    return stackPopCount;
  }
  public void visitConstantInstruction(
      Clazz clazz,
      Method method,
      CodeAttribute codeAttribute,
      int offset,
      ConstantInstruction constantInstruction) {
    byte opcode = constantInstruction.opcode;

    // Check for instructions that involve fields.
    switch (opcode) {
      case InstructionConstants.OP_LDC:
      case InstructionConstants.OP_LDC_W:
        // Mark the field, if any, as being read from and written to.
        reading = true;
        writing = true;
        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
        break;

      case InstructionConstants.OP_GETSTATIC:
      case InstructionConstants.OP_GETFIELD:
        // Mark the field as being read from.
        reading = true;
        writing = false;
        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
        break;

      case InstructionConstants.OP_PUTSTATIC:
      case InstructionConstants.OP_PUTFIELD:
        // Mark the field as being written to.
        reading = false;
        writing = true;
        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
        break;
    }
  }
 /** Applies the given visitor to all referenced classes. */
 public void referencedClassesAccept(ClassVisitor classVisitor) {
   if (referencedClasses != null) {
     for (int index = 0; index < referencedClasses.length; index++) {
       Clazz referencedClass = referencedClasses[index];
       if (referencedClass != null) {
         referencedClass.accept(classVisitor);
       }
     }
   }
 }
Exemple #6
0
  public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) {
    // At this point, we only mark outer classes of this class.
    // Inner class can be marked later, by InnerUsageMarker.
    if (innerClassesInfo.u2innerClassIndex != 0
        && clazz.getName().equals(clazz.getClassName(innerClassesInfo.u2innerClassIndex))) {
      markAsUsed(innerClassesInfo);

      innerClassesInfo.innerClassConstantAccept(clazz, this);
      innerClassesInfo.outerClassConstantAccept(clazz, this);
      innerClassesInfo.innerNameConstantAccept(clazz, this);
    }
  }
  public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) {
    Clazz referencedClass = refConstant.referencedClass;
    if (referencedClass != null
        && (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) {
      setInvokesPackageVisibleMembers(clazz);
    }

    Member referencedMember = refConstant.referencedMember;
    if (referencedMember != null
        && (referencedMember.getAccessFlags()
                & (ClassConstants.INTERNAL_ACC_PUBLIC | ClassConstants.INTERNAL_ACC_PRIVATE))
            == 0) {
      setInvokesPackageVisibleMembers(clazz);
    }
  }
Exemple #8
0
  public void read(DataEntry dataEntry) throws IOException {
    try {
      // Get the input stream.
      InputStream inputStream = dataEntry.getInputStream();

      // Wrap it into a data input stream.
      DataInputStream dataInputStream = new DataInputStream(inputStream);

      // Create a Clazz representation.
      Clazz clazz;
      if (isLibrary) {
        clazz = new LibraryClass();
        clazz.accept(
            new LibraryClassReader(
                dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers));
      } else {
        clazz = new ProgramClass();
        clazz.accept(new ProgramClassReader(dataInputStream));
      }

      // Apply the visitor, if we have a real class.
      String className = clazz.getName();
      if (className != null) {
        if (!dataEntry
                .getName()
                .replace(File.pathSeparatorChar, ClassConstants.PACKAGE_SEPARATOR)
                .equals(className + ClassConstants.CLASS_FILE_EXTENSION)
            && warningPrinter != null) {
          warningPrinter.print(
              className,
              "Warning: class ["
                  + dataEntry.getName()
                  + "] unexpectedly contains class ["
                  + ClassUtil.externalClassName(className)
                  + "]");
        }

        clazz.accept(classVisitor);
      }

      dataEntry.closeInputStream();
    } catch (Exception ex) {
      throw (IOException)
          new IOException(
                  "Can't process class [" + dataEntry.getName() + "] (" + ex.getMessage() + ")")
              .initCause(ex);
    }
  }
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    // TODO: Remove this when the code has stabilized.
    // Catch any unexpected exceptions from the actual visiting method.
    try {
      // Process the code.
      visitCodeAttribute0(clazz, method, codeAttribute);
    } catch (RuntimeException ex) {
      System.err.println("Unexpected error while computing stack sizes:");
      System.err.println("  Class       = [" + clazz.getName() + "]");
      System.err.println(
          "  Method      = [" + method.getName(clazz) + method.getDescriptor(clazz) + "]");
      System.err.println(
          "  Exception   = [" + ex.getClass().getName() + "] (" + ex.getMessage() + ")");

      if (DEBUG) {
        method.accept(clazz, new ClassPrinter());
      }

      throw ex;
    }
  }
Exemple #10
0
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    // The minimum variable size is determined by the arguments.
    codeAttribute.u2maxLocals =
        ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags());

    if (DEBUG) {
      System.out.println(
          "VariableSizeUpdater: "
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz));
      System.out.println("  Max locals: " + codeAttribute.u2maxLocals + " <- parameters");
    }

    // Go over all instructions.
    codeAttribute.instructionsAccept(clazz, method, this);

    // Remove the unused variables of the attributes.
    codeAttribute.attributesAccept(clazz, method, variableCleaner);
  }
Exemple #11
0
  /**
   * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
   * any.
   */
  protected void markMethodHierarchy(Clazz clazz, Method method) {
    int accessFlags = method.getAccessFlags();
    if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0
        && !ClassUtil.isInitializer(method.getName(clazz))) {
      // We can skip private and static methods in the hierarchy, and
      // also abstract methods, unless they might widen a current
      // non-public access.
      int requiredUnsetAccessFlags =
          ClassConstants.ACC_PRIVATE
              | ClassConstants.ACC_STATIC
              | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT);

      clazz.accept(
          new ConcreteClassDownTraveler(
              new ClassHierarchyTraveler(
                  true,
                  true,
                  false,
                  true,
                  new NamedMethodVisitor(
                      method.getName(clazz),
                      method.getDescriptor(clazz),
                      new MemberAccessFilter(0, requiredUnsetAccessFlags, this)))));
    }
  }
  public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    if (DEBUG) {
      System.out.println(
          "StackSizeComputer: "
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz));
    }

    // Try to reuse the previous array.
    int codeLength = codeAttribute.u4codeLength;
    if (evaluated.length < codeLength) {
      evaluated = new boolean[codeLength];
      stackSizes = new int[codeLength];
    } else {
      Arrays.fill(evaluated, 0, codeLength, false);
    }

    // The initial stack is always empty.
    stackSize = 0;
    maxStackSize = 0;

    // Evaluate the instruction block starting at the entry point of the method.
    evaluateInstructionBlock(clazz, method, codeAttribute, 0);

    // Evaluate the exception handlers.
    codeAttribute.exceptionsAccept(clazz, method, this);
  }
Exemple #13
0
  /**
   * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
   * any.
   */
  protected void markMethodHierarchy(Clazz clazz, Method method) {
    int accessFlags = method.getAccessFlags();
    if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0
        && !ClassUtil.isInitializer(method.getName(clazz))) {
      // We can skip private and static methods in the hierarchy, and
      // also abstract methods, unless they might widen a current
      // non-public access.
      int requiredUnsetAccessFlags =
          ClassConstants.ACC_PRIVATE
              | ClassConstants.ACC_STATIC
              | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT);

      // Mark default implementations in interfaces down the hierarchy.
      // TODO: This may be premature if there aren't any concrete implementing classes.
      clazz.accept(
          new ClassAccessFilter(
              ClassConstants.ACC_ABSTRACT,
              0,
              new ClassHierarchyTraveler(
                  false,
                  false,
                  false,
                  true,
                  new ProgramClassFilter(
                      new ClassAccessFilter(
                          ClassConstants.ACC_ABSTRACT,
                          0,
                          new NamedMethodVisitor(
                              method.getName(clazz),
                              method.getDescriptor(clazz),
                              new MemberAccessFilter(
                                  0, requiredUnsetAccessFlags, defaultMethodUsageMarker)))))));

      // Mark other implementations.
      clazz.accept(
          new ConcreteClassDownTraveler(
              new ClassHierarchyTraveler(
                  true,
                  true,
                  false,
                  true,
                  new NamedMethodVisitor(
                      method.getName(clazz),
                      method.getDescriptor(clazz),
                      new MemberAccessFilter(0, requiredUnsetAccessFlags, this)))));
    }
  }
Exemple #14
0
  public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) {
    // Process the generic definitions, superclass, and implemented
    // interfaces.
    String signature = clazz.getString(signatureAttribute.u2signatureIndex);

    // Count the signature types.
    InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(signature);

    int count = 0;
    int interfacesCount = -1;
    while (internalTypeEnumeration.hasMoreTypes()) {
      String internalType = internalTypeEnumeration.nextType();

      count++;

      if (ClassUtil.isInternalClassType(internalType)) {
        interfacesCount++;
      }
    }

    // Put the signature types in an array.
    internalTypeEnumeration = new InternalTypeEnumeration(signature);

    String[] internalTypes = new String[count];

    for (int index = 0; index < count; index++) {
      String internalType = internalTypeEnumeration.nextType();

      internalTypes[index] = internalType;
    }

    // Sort the interface types in the array.
    Arrays.sort(internalTypes, count - interfacesCount, count);

    // Recompose the signature types in a string.
    StringBuffer newSignatureBuffer = new StringBuffer();

    for (int index = 0; index < count; index++) {
      // Is this not an interface type, or an interface type that isn't
      // a duplicate of the previous interface type?
      if (index < count - interfacesCount
          || !internalTypes[index].equals(internalTypes[index - 1])) {
        newSignatureBuffer.append(internalTypes[index]);
      }
    }

    String newSignature = newSignatureBuffer.toString();

    // Did the signature change?
    if (!newSignature.equals(signature)) {
      // Update the signature.
      ((Utf8Constant) ((ProgramClass) clazz).constantPool[signatureAttribute.u2signatureIndex])
          .setString(newSignatureBuffer.toString());

      // Clear the referenced classes.
      // TODO: Properly update the referenced classes.
      signatureAttribute.referencedClasses = null;
    }
  }
 public String toString() {
   return "certain="
       + certain
       + ", depth="
       + depth
       + ": "
       + reason
       + (clazz != null ? clazz.getName() : "(none)")
       + ": "
       + (member != null ? member.getName(clazz) : "(none)");
 }
 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
   targetClass.hierarchyAccept(
       visitThisMember,
       visitSuperMembers,
       visitInterfaceMembers,
       visitOverridingMembers,
       new NamedMethodVisitor(
           programMethod.getName(programClass),
           programMethod.getDescriptor(programClass),
           memberVisitor));
 }
 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {
   targetClass.hierarchyAccept(
       visitThisMember,
       visitSuperMembers,
       visitInterfaceMembers,
       visitOverridingMembers,
       new NamedFieldVisitor(
           libraryField.getName(libraryClass),
           libraryField.getDescriptor(libraryClass),
           memberVisitor));
 }
Exemple #18
0
  public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) {
    if (shouldBeMarkedAsUsed(invokeDynamicConstant)) {
      markAsUsed(invokeDynamicConstant);

      markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex);

      // Mark the bootstrap methods attribute.
      clazz.attributesAccept(
          new MyBootStrapMethodUsageMarker(invokeDynamicConstant.u2bootstrapMethodAttributeIndex));
    }
  }
Exemple #19
0
  public void visitConstantInstruction(
      Clazz clazz,
      Method method,
      CodeAttribute codeAttribute,
      int offset,
      ConstantInstruction constantInstruction) {
    markConstant(clazz, constantInstruction.constantIndex);

    // Also mark the parameterless constructor of the class, in case the
    // string constant or class constant is being used in a Class.forName
    // or a .class construct.
    clazz.constantPoolEntryAccept(
        constantInstruction.constantIndex, parameterlessConstructorMarker);
  }
  public void visitSignatureAttribute(
      Clazz clazz, Method method, SignatureAttribute signatureAttribute) {
    // Compute the new signature.
    String signature = clazz.getString(signatureAttribute.u2signatureIndex);
    String newSignature = shrinkDescriptor(method, signature);

    // Update the signature.
    signatureAttribute.u2signatureIndex =
        new ConstantPoolEditor((ProgramClass) clazz).addUtf8Constant(newSignature);

    // Update the referenced classes.
    signatureAttribute.referencedClasses =
        shrinkReferencedClasses(method, signature, signatureAttribute.referencedClasses);
  }
  /** Returns if the number of superclasses of the given class in the given set of classes. */
  private int superClassCount(Clazz subClass, Set classes) {
    int count = 0;

    Iterator iterator = classes.iterator();

    while (iterator.hasNext()) {
      Clazz clazz = (Clazz) iterator.next();
      if (subClass.extendsOrImplements(clazz)) {
        count++;
      }
    }

    return count;
  }
  public int stackPushCount(Clazz clazz) {
    int stackPushCount = super.stackPushCount(clazz);

    // Some special cases.
    switch (opcode) {
      case InstructionConstants.OP_GETSTATIC:
      case InstructionConstants.OP_GETFIELD:
      case InstructionConstants.OP_INVOKEVIRTUAL:
      case InstructionConstants.OP_INVOKESPECIAL:
      case InstructionConstants.OP_INVOKESTATIC:
      case InstructionConstants.OP_INVOKEINTERFACE:
        // The field value or a return value may be pushed onto the stack.
        clazz.constantPoolEntryAccept(constantIndex, this);
        stackPushCount += typeStackDelta;
        break;
    }

    return stackPushCount;
  }
Exemple #23
0
 /**
  * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
  * any.
  */
 protected void markMethodHierarchy(Clazz clazz, Method method) {
   if ((method.getAccessFlags()
           & (ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC))
       == 0) {
     clazz.accept(
         new ConcreteClassDownTraveler(
             new ClassHierarchyTraveler(
                 true,
                 true,
                 false,
                 true,
                 new NamedMethodVisitor(
                     method.getName(clazz),
                     method.getDescriptor(clazz),
                     new MemberAccessFilter(
                         0,
                         ClassConstants.INTERNAL_ACC_PRIVATE
                             | ClassConstants.INTERNAL_ACC_STATIC
                             | ClassConstants.INTERNAL_ACC_ABSTRACT,
                         this)))));
   }
 }
 public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) {
   clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, methodrefConstantVisitor);
 }
  /**
   * Returns the most specific common superclass or interface of the given classes.
   *
   * @param class1 the first class.
   * @param class2 the second class.
   * @param interfaces specifies whether to look for a superclass or for an interface.
   * @return the common class.
   */
  private Clazz findCommonClass(Clazz class1, Clazz class2, boolean interfaces) {
    // Collect the superclasses or the interfaces of this class.
    Set superClasses1 = new HashSet();
    class1.hierarchyAccept(
        !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses1));

    int superClasses1Count = superClasses1.size();
    if (superClasses1Count == 0) {
      if (interfaces) {
        return null;
      } else if (class1.getSuperName() != null) {
        throw new IllegalArgumentException(
            "Can't find any super classes of ["
                + class1.getName()
                + "] (not even immediate super class ["
                + class1.getSuperName()
                + "])");
      }
    }

    // Collect the superclasses or the interfaces of the other class.
    Set superClasses2 = new HashSet();
    class2.hierarchyAccept(
        !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses2));

    int superClasses2Count = superClasses2.size();
    if (superClasses2Count == 0) {
      if (interfaces) {
        return null;
      } else if (class2.getSuperName() != null) {
        throw new IllegalArgumentException(
            "Can't find any super classes of ["
                + class2.getName()
                + "] (not even immediate super class ["
                + class2.getSuperName()
                + "])");
      }
    }

    if (DEBUG) {
      System.out.println(
          "ReferenceValue.generalize this ["
              + class1.getName()
              + "] with other ["
              + class2.getName()
              + "] (interfaces = "
              + interfaces
              + ")");
      System.out.println("  This super classes:  " + superClasses1);
      System.out.println("  Other super classes: " + superClasses2);
    }

    // Find the common superclasses.
    superClasses1.retainAll(superClasses2);

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

    if (interfaces && superClasses1.isEmpty()) {
      return null;
    }

    // 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 = superClasses1.iterator();
    while (commonSuperClasses.hasNext()) {
      Clazz commonSuperClass = (Clazz) commonSuperClasses.next();

      int superClassCount = superClassCount(commonSuperClass, superClasses1);
      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 ["
              + class1.getName()
              + "] (with "
              + superClasses1Count
              + " known super classes) and ["
              + class2.getName()
              + "] (with "
              + superClasses2Count
              + " known super classes)");
    }

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

    return commonClass;
  }
 /**
  * Applies the given class visitor to this mark's class, if any, and if this mark doesn't have a
  * member.
  */
 public void acceptClassVisitor(ClassVisitor classVisitor) {
   if (clazz != null && member == null) {
     clazz.accept(classVisitor);
   }
 }
 /** Returns whether this is mark is caused by the given class. */
 public boolean isCausedBy(Clazz clazz) {
   return clazz.equals(this.clazz);
 }
  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;
  }
  /**
   * Evaluates a block of instructions that hasn't been handled before, starting at the given offset
   * and ending at a branch instruction, a return instruction, or a throw instruction. Branch
   * instructions are handled recursively.
   */
  private void evaluateInstructionBlock(
      Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset) {
    if (DEBUG) {
      if (evaluated[instructionOffset]) {
        System.out.println("-- (instruction block at " + instructionOffset + " already evaluated)");
      } else {
        System.out.println("-- instruction block:");
      }
    }

    // Remember the initial stack size.
    int initialStackSize = stackSize;

    // Remember the maximum stack size.
    if (maxStackSize < stackSize) {
      maxStackSize = stackSize;
    }

    // Evaluate any instructions that haven't been evaluated before.
    while (!evaluated[instructionOffset]) {
      // Mark the instruction as evaluated.
      evaluated[instructionOffset] = true;

      Instruction instruction = InstructionFactory.create(codeAttribute.code, instructionOffset);

      if (DEBUG) {
        int stackPushCount = instruction.stackPushCount(clazz);
        int stackPopCount = instruction.stackPopCount(clazz);
        System.out.println(
            "["
                + instructionOffset
                + "]: "
                + stackSize
                + " - "
                + stackPopCount
                + " + "
                + stackPushCount
                + " = "
                + (stackSize + stackPushCount - stackPopCount)
                + ": "
                + instruction.toString(instructionOffset));
      }

      // Compute the instruction's effect on the stack size.
      stackSize -= instruction.stackPopCount(clazz);

      if (stackSize < 0) {
        throw new IllegalArgumentException(
            "Stack size becomes negative after instruction "
                + instruction.toString(instructionOffset)
                + " in ["
                + clazz.getName()
                + "."
                + method.getName(clazz)
                + method.getDescriptor(clazz)
                + "]");
      }

      stackSizes[instructionOffset] = stackSize += instruction.stackPushCount(clazz);

      // Remember the maximum stack size.
      if (maxStackSize < stackSize) {
        maxStackSize = stackSize;
      }

      // Remember the next instruction offset.
      int nextInstructionOffset = instructionOffset + instruction.length(instructionOffset);

      // Visit the instruction, in order to handle branches.
      instruction.accept(clazz, method, codeAttribute, instructionOffset, this);

      // Stop evaluating after a branch.
      if (exitInstructionBlock) {
        break;
      }

      // Continue with the next instruction.
      instructionOffset = nextInstructionOffset;

      if (DEBUG) {
        if (evaluated[instructionOffset]) {
          System.out.println("-- (instruction at " + instructionOffset + " already evaluated)");
        }
      }
    }

    // Restore the stack size for possible subsequent instruction blocks.
    this.stackSize = initialStackSize;
  }
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    if (DEBUG) {
      method.accept(clazz, new ClassPrinter());
    }

    branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);

    // Don't bother if there aren't any subroutines anyway.
    if (!containsSubroutines(codeAttribute)) {
      return;
    }

    if (DEBUG) {
      System.out.println(
          "SubroutineInliner: processing ["
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz)
              + "]");
    }

    // Append the body of the code.
    codeAttributeComposer.reset();
    codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);

    // Copy the non-subroutine instructions.
    int offset = 0;
    while (offset < codeAttribute.u4codeLength) {
      Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
      int instructionLength = instruction.length(offset);

      // Is this returning subroutine?
      if (branchTargetFinder.isSubroutine(offset)
          && branchTargetFinder.isSubroutineReturning(offset)) {
        // Skip the subroutine.
        if (DEBUG) {
          System.out.println(
              "  Skipping original subroutine instruction " + instruction.toString(offset));
        }

        // Append a label at this offset instead.
        codeAttributeComposer.appendLabel(offset);
      } else {
        // Copy the instruction, inlining any subroutine call recursively.
        instruction.accept(clazz, method, codeAttribute, offset, this);
      }

      offset += instructionLength;
    }

    // Copy the exceptions. Note that exceptions with empty try blocks
    // are automatically removed.
    codeAttribute.exceptionsAccept(clazz, method, subroutineExceptionInliner);

    if (DEBUG) {
      System.out.println("  Appending label after code at [" + offset + "]");
    }

    // Append a label just after the code.
    codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);

    // End and update the code attribute.
    codeAttributeComposer.endCodeFragment();
    codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);

    if (DEBUG) {
      method.accept(clazz, new ClassPrinter());
    }
  }