/** Returns the ClassMemberSpecification currently represented in this dialog. */
  public ClassMemberSpecification getClassMemberSpecification() {
    String name = nameTextField.getText();
    String type = typeTextField.getText();
    String arguments = argumentsTextField.getText();

    if (name.equals("") || name.equals("*")) {
      name = null;
    }

    if (type.equals("") || type.equals("*")) {
      type = null;
    }

    if (name != null || type != null) {
      if (isField) {
        if (type == null) {
          type = ClassConstants.EXTERNAL_TYPE_INT;
        }

        type = ClassUtil.internalType(type);
      } else {
        if (type == null) {
          type = ClassConstants.EXTERNAL_TYPE_VOID;
        }

        type = ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments));
      }
    }

    ClassMemberSpecification classMemberSpecification =
        new ClassMemberSpecification(0, 0, name, type);

    // Also get the access radio button settings.
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC, publicRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE, privateRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED, protectedRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC, staticRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE, volatileRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT, transientRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification,
        ClassConstants.INTERNAL_ACC_SYNCHRONIZED,
        synchronizedRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE, nativeRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons);
    getClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT, strictRadioButtons);

    return classMemberSpecification;
  }
  public void visitProgramClass(ProgramClass programClass) {
    String name = programClass.getName();
    String newName = ClassObfuscator.newClassName(programClass);

    ps.println(
        ClassUtil.externalClassName(name) + " -> " + ClassUtil.externalClassName(newName) + ":");

    // Print out the class members.
    programClass.fieldsAccept(this);
    programClass.methodsAccept(this);
  }
예제 #3
0
  private void writeMethodSpecification(List memberSpecifications) {
    if (memberSpecifications != null) {
      for (int index = 0; index < memberSpecifications.size(); index++) {
        MemberSpecification memberSpecification =
            (MemberSpecification) memberSpecifications.get(index);

        writer.print("    ");

        // Write out the required annotation, if any.
        if (memberSpecification.annotationType != null) {
          writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
          writer.println(ClassUtil.externalType(memberSpecification.annotationType));
          writer.print("    ");
        }

        // Write out the method access flags.
        writer.print(
            ClassUtil.externalMethodAccessFlags(
                memberSpecification.requiredUnsetAccessFlags,
                ConfigurationConstants.NEGATOR_KEYWORD));

        writer.print(
            ClassUtil.externalMethodAccessFlags(memberSpecification.requiredSetAccessFlags));

        // Write out the method name and descriptor.
        String name = memberSpecification.name;
        String descriptor = memberSpecification.descriptor;

        writer.print(
            descriptor == null
                ? name == null
                    ? ConfigurationConstants.ANY_METHOD_KEYWORD
                    : ConfigurationConstants.ANY_TYPE_KEYWORD
                        + ' '
                        + name
                        + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD
                        + ConfigurationConstants.ANY_ARGUMENTS_KEYWORD
                        + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD
                : ClassUtil.externalFullMethodDescription(
                    ClassConstants.METHOD_NAME_INIT,
                    0,
                    name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name,
                    descriptor));

        writer.println(ConfigurationConstants.SEPARATOR_KEYWORD);
      }
    }
  }
예제 #4
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);
  }
 public void visitLibraryClass(LibraryClass libraryClass) {
   notePrinter.print(
       libraryClass.getName(),
       "Note: duplicate definition of library class ["
           + ClassUtil.externalClassName(libraryClass.getName())
           + "]");
 }
  /** Returns a shrunk descriptor or signature of the given method. */
  private String shrinkDescriptor(Method method, String descriptor) {
    // All parameters of non-static methods are shifted by one in the local
    // variable frame.
    int parameterIndex =
        (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

    // Go over the parameters.
    InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

    StringBuffer newDescriptorBuffer = new StringBuffer();

    newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters());
    newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);

    while (internalTypeEnumeration.hasMoreTypes()) {
      String type = internalTypeEnumeration.nextType();
      if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
        newDescriptorBuffer.append(type);
      } else if (DEBUG) {
        System.out.println("  Deleting parameter #" + parameterIndex + " [" + type + "]");
      }

      parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    }

    newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
    newDescriptorBuffer.append(internalTypeEnumeration.returnType());

    return newDescriptorBuffer.toString();
  }
예제 #7
0
 public ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) {
   return type == null
       ? ValueFactory.REFERENCE_VALUE_NULL
       : !ClassUtil.isInternalArrayType(type)
           ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL
           : valueFactory.createValue(type.substring(1), referencedClass, true).referenceValue();
 }
예제 #8
0
 public final String internalType() {
   return type == null
       ? ClassConstants.TYPE_JAVA_LANG_OBJECT
       : ClassUtil.isInternalArrayType(type)
           ? type
           : ClassConstants.TYPE_CLASS_START + type + ClassConstants.TYPE_CLASS_END;
 }
예제 #9
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)))));
    }
  }
예제 #10
0
 public void visitProgramClass(ProgramClass programClass) {
   notePrinter.print(
       programClass.getName(),
       "Note: duplicate definition of program class ["
           + ClassUtil.externalClassName(programClass.getName())
           + "]");
 }
예제 #11
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;
    }
  }
예제 #12
0
  /** Returns a list with external versions of the given list of internal class names. */
  private List externalClassNames(List internalClassNames) {
    List externalClassNames = new ArrayList(internalClassNames.size());

    for (int index = 0; index < internalClassNames.size(); index++) {
      externalClassNames.add(ClassUtil.externalClassName((String) internalClassNames.get(index)));
    }

    return externalClassNames;
  }
  /** Shrinks the array of referenced classes of the given method. */
  private Clazz[] shrinkReferencedClasses(
      Method method, String descriptor, Clazz[] referencedClasses) {
    if (referencedClasses != null) {
      // All parameters of non-static methods are shifted by one in the local
      // variable frame.
      int parameterIndex =
          (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

      int referencedClassIndex = 0;
      int newReferencedClassIndex = 0;

      // Go over the parameters.
      InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

      // Also look at the formal type parameters.
      String type = internalTypeEnumeration.formalTypeParameters();
      int count = new DescriptorClassEnumeration(type).classCount();
      for (int counter = 0; counter < count; counter++) {
        referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
      }

      while (internalTypeEnumeration.hasMoreTypes()) {
        // Consider the classes referenced by this parameter type.
        type = internalTypeEnumeration.nextType();
        count = new DescriptorClassEnumeration(type).classCount();

        if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
          // Copy the referenced classes.
          for (int counter = 0; counter < count; counter++) {
            referencedClasses[newReferencedClassIndex++] =
                referencedClasses[referencedClassIndex++];
          }
        } else {
          // Skip the referenced classes.
          referencedClassIndex += count;
        }

        parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
      }

      // Also look at the return value.
      type = internalTypeEnumeration.returnType();
      count = new DescriptorClassEnumeration(type).classCount();
      for (int counter = 0; counter < count; counter++) {
        referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
      }

      // Clear the unused entries.
      while (newReferencedClassIndex < referencedClassIndex) {
        referencedClasses[newReferencedClassIndex++] = null;
      }
    }

    return referencedClasses;
  }
  /** Sets the ClassMemberSpecification to be represented in this dialog. */
  public void setClassMemberSpecification(ClassMemberSpecification classMemberSpecification) {
    String name = classMemberSpecification.name;
    String descriptor = classMemberSpecification.descriptor;

    // Set the access radio buttons.
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC, publicRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE, privateRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED, protectedRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC, staticRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE, volatileRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT, transientRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification,
        ClassConstants.INTERNAL_ACC_SYNCHRONIZED,
        synchronizedRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE, nativeRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons);
    setClassMemberSpecificationRadioButtons(
        classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT, strictRadioButtons);

    // Set the class name text fields.
    nameTextField.setText(name == null ? "" : name);

    if (isField) {
      typeTextField.setText(descriptor == null ? "" : ClassUtil.externalType(descriptor));
    } else {
      typeTextField.setText(
          descriptor == null ? "" : ClassUtil.externalMethodReturnType(descriptor));
      argumentsTextField.setText(
          descriptor == null ? "" : ClassUtil.externalMethodArguments(descriptor));
    }
  }
예제 #15
0
  private void writeOption(String optionName, String arguments, boolean replaceInternalClassNames) {
    if (arguments != null) {
      if (replaceInternalClassNames) {
        arguments = ClassUtil.externalClassName(arguments);
      }

      writer.print(optionName);
      writer.print(' ');
      writer.println(quotedString(arguments));
    }
  }
 public void visitProgramField(ProgramClass programClass, ProgramField programField) {
   String newName = MemberObfuscator.newMemberName(programField);
   if (newName != null) {
     ps.println(
         "    "
             +
             // lineNumberRange(programClass, programField) +
             ClassUtil.externalFullFieldDescription(
                 0, programField.getName(programClass), programField.getDescriptor(programClass))
             + " -> "
             + newName);
   }
 }
예제 #17
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);
    }
  }
예제 #18
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)))));
    }
  }
  public void visitAnyParameterAnnotationsAttribute(
      Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) {
    int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
    Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations;

    // All parameters of non-static methods are shifted by one in the local
    // variable frame.
    int parameterIndex =
        (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

    int annotationIndex = 0;
    int newAnnotationIndex = 0;

    // Go over the parameters.
    String descriptor = method.getDescriptor(clazz);
    InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

    while (internalTypeEnumeration.hasMoreTypes()) {
      String type = internalTypeEnumeration.nextType();
      if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
        annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
        annotations[newAnnotationIndex++] = annotations[annotationIndex];
      }

      annotationIndex++;

      parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    }

    // Update the number of parameters.
    parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;

    // Clear the unused entries.
    while (newAnnotationIndex < annotationIndex) {
      annotationsCounts[newAnnotationIndex] = 0;
      annotations[newAnnotationIndex++] = null;
    }
  }
  public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) {
    // Special cases: <clinit> and <init> are always kept unchanged.
    // We can ignore them here.
    String name = programMethod.getName(programClass);
    if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)
        || name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) {
      return;
    }

    String newName = MemberObfuscator.newMemberName(programMethod);
    if (newName != null) {
      ps.println(
          "    "
              + lineNumberRange(programClass, programMethod)
              + ClassUtil.externalFullMethodDescription(
                  programClass.getName(),
                  0,
                  programMethod.getName(programClass),
                  programMethod.getDescriptor(programClass))
              + " -> "
              + newName);
    }
  }
  public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) {
    String type = fieldrefConstant.getType(clazz);

    typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
  }
예제 #22
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;
  }
  private void visitRefConstant(Clazz clazz, RefConstant methodrefConstant) {
    String type = methodrefConstant.getType(clazz);

    parameterStackDelta = ClassUtil.internalMethodParameterSize(type);
    typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
  }
예제 #24
0
  private void writeOption(String optionName, ClassSpecification classSpecification) {
    writer.println();

    // Write out the comments for this option.
    writeComments(classSpecification.comments);

    writer.print(optionName);
    writer.print(' ');

    // Write out the required annotation, if any.
    if (classSpecification.annotationType != null) {
      writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
      writer.print(ClassUtil.externalType(classSpecification.annotationType));
      writer.print(' ');
    }

    // Write out the class access flags.
    writer.print(
        ClassUtil.externalClassAccessFlags(
            classSpecification.requiredUnsetAccessFlags, ConfigurationConstants.NEGATOR_KEYWORD));

    writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredSetAccessFlags));

    // Write out the class keyword, if we didn't write the interface
    // keyword earlier.
    if (((classSpecification.requiredSetAccessFlags | classSpecification.requiredUnsetAccessFlags)
            & (ClassConstants.ACC_INTERFACE | ClassConstants.ACC_ENUM))
        == 0) {
      writer.print(ConfigurationConstants.CLASS_KEYWORD);
    }

    writer.print(' ');

    // Write out the class name.
    writer.print(
        classSpecification.className != null
            ? ClassUtil.externalClassName(classSpecification.className)
            : ConfigurationConstants.ANY_CLASS_KEYWORD);

    // Write out the extends template, if any.
    if (classSpecification.extendsAnnotationType != null
        || classSpecification.extendsClassName != null) {
      writer.print(' ');
      writer.print(ConfigurationConstants.EXTENDS_KEYWORD);
      writer.print(' ');

      // Write out the required extends annotation, if any.
      if (classSpecification.extendsAnnotationType != null) {
        writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
        writer.print(ClassUtil.externalType(classSpecification.extendsAnnotationType));
        writer.print(' ');
      }

      // Write out the extended class name.
      writer.print(
          classSpecification.extendsClassName != null
              ? ClassUtil.externalClassName(classSpecification.extendsClassName)
              : ConfigurationConstants.ANY_CLASS_KEYWORD);
    }

    // Write out the keep field and keep method options, if any.
    if (classSpecification.fieldSpecifications != null
        || classSpecification.methodSpecifications != null) {
      writer.print(' ');
      writer.println(ConfigurationConstants.OPEN_KEYWORD);

      writeFieldSpecification(classSpecification.fieldSpecifications);
      writeMethodSpecification(classSpecification.methodSpecifications);

      writer.println(ConfigurationConstants.CLOSE_KEYWORD);
    } else {
      writer.println();
    }
  }
예제 #25
0
  /** Performs the subsequent ReTrace operations. */
  public void execute() throws IOException {
    // Read the mapping file.
    MappingReader mappingReader = new MappingReader(mappingFile);
    mappingReader.pump(this);

    StringBuffer expressionBuffer = new StringBuffer(regularExpression.length() + 32);
    char[] expressionTypes = new char[32];
    int expressionTypeCount = 0;
    int index = 0;
    while (true) {
      int nextIndex = regularExpression.indexOf('%', index);
      if (nextIndex < 0
          || nextIndex == regularExpression.length() - 1
          || expressionTypeCount == expressionTypes.length) {
        break;
      }

      expressionBuffer.append(regularExpression.substring(index, nextIndex));
      expressionBuffer.append('(');

      char expressionType = regularExpression.charAt(nextIndex + 1);
      switch (expressionType) {
        case 'c':
          expressionBuffer.append(REGEX_CLASS);
          break;

        case 'C':
          expressionBuffer.append(REGEX_CLASS_SLASH);
          break;

        case 'l':
          expressionBuffer.append(REGEX_LINE_NUMBER);
          break;

        case 't':
          expressionBuffer.append(REGEX_TYPE);
          break;

        case 'f':
          expressionBuffer.append(REGEX_MEMBER);
          break;

        case 'm':
          expressionBuffer.append(REGEX_MEMBER);
          break;

        case 'a':
          expressionBuffer.append(REGEX_ARGUMENTS);
          break;
      }

      expressionBuffer.append(')');

      expressionTypes[expressionTypeCount++] = expressionType;

      index = nextIndex + 2;
    }

    expressionBuffer.append(regularExpression.substring(index));

    Pattern pattern = Pattern.compile(expressionBuffer.toString());

    // Read the stack trace file.
    LineNumberReader reader =
        new LineNumberReader(
            stackTraceFile == null
                ? (Reader) new InputStreamReader(System.in)
                : (Reader) new BufferedReader(new FileReader(stackTraceFile)));

    try {
      StringBuffer outLine = new StringBuffer(256);
      List extraOutLines = new ArrayList();

      String className = null;

      // Read the line in the stack trace.
      while (true) {
        String line = reader.readLine();
        if (line == null) {
          break;
        }

        Matcher matcher = pattern.matcher(line);

        if (matcher.matches()) {
          int lineNumber = 0;
          String type = null;
          String arguments = null;

          // Figure out a class name, line number, type, and
          // arguments beforehand.
          for (int expressionTypeIndex = 0;
              expressionTypeIndex < expressionTypeCount;
              expressionTypeIndex++) {
            int startIndex = matcher.start(expressionTypeIndex + 1);
            if (startIndex >= 0) {
              String match = matcher.group(expressionTypeIndex + 1);

              char expressionType = expressionTypes[expressionTypeIndex];
              switch (expressionType) {
                case 'c':
                  className = originalClassName(match);
                  break;

                case 'C':
                  className = originalClassName(ClassUtil.externalClassName(match));
                  break;

                case 'l':
                  lineNumber = Integer.parseInt(match);
                  break;

                case 't':
                  type = originalType(match);
                  break;

                case 'a':
                  arguments = originalArguments(match);
                  break;
              }
            }
          }

          // Actually construct the output line.
          int lineIndex = 0;

          outLine.setLength(0);
          extraOutLines.clear();

          for (int expressionTypeIndex = 0;
              expressionTypeIndex < expressionTypeCount;
              expressionTypeIndex++) {
            int startIndex = matcher.start(expressionTypeIndex + 1);
            if (startIndex >= 0) {
              int endIndex = matcher.end(expressionTypeIndex + 1);
              String match = matcher.group(expressionTypeIndex + 1);

              // Copy a literal piece of input line.
              outLine.append(line.substring(lineIndex, startIndex));

              char expressionType = expressionTypes[expressionTypeIndex];
              switch (expressionType) {
                case 'c':
                  className = originalClassName(match);
                  outLine.append(className);
                  break;

                case 'C':
                  className = originalClassName(ClassUtil.externalClassName(match));
                  outLine.append(ClassUtil.internalClassName(className));
                  break;

                case 'l':
                  lineNumber = Integer.parseInt(match);
                  outLine.append(match);
                  break;

                case 't':
                  type = originalType(match);
                  outLine.append(type);
                  break;

                case 'f':
                  originalFieldName(className, match, type, outLine, extraOutLines);
                  break;

                case 'm':
                  originalMethodName(
                      className, match, lineNumber, type, arguments, outLine, extraOutLines);
                  break;

                case 'a':
                  arguments = originalArguments(match);
                  outLine.append(arguments);
                  break;
              }

              // Skip the original element whose processed version
              // has just been appended.
              lineIndex = endIndex;
            }
          }

          // Copy the last literal piece of input line.
          outLine.append(line.substring(lineIndex));

          // Print out the main line.
          System.out.println(outLine);

          // Print out any additional lines.
          for (int extraLineIndex = 0; extraLineIndex < extraOutLines.size(); extraLineIndex++) {
            System.out.println(extraOutLines.get(extraLineIndex));
          }
        } else {
          // Print out the original line.
          System.out.println(line);
        }
      }
    } catch (IOException ex) {
      throw new IOException("Can't read stack trace (" + ex.getMessage() + ")");
    } finally {
      if (stackTraceFile != null) {
        try {
          reader.close();
        } catch (IOException ex) {
          // This shouldn't happen.
        }
      }
    }
  }
예제 #26
0
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    // Get the original parameter size that was saved.
    int oldParameterSize = ParameterUsageMarker.getParameterSize(method);

    // Compute the new parameter size from the shrunk descriptor.
    int newParameterSize =
        ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags());

    if (oldParameterSize > newParameterSize) {
      // Get the total size of the local variable frame.
      int maxLocals = codeAttribute.u2maxLocals;

      if (DEBUG) {
        System.out.println(
            "ParameterShrinker: "
                + clazz.getName()
                + "."
                + method.getName(clazz)
                + method.getDescriptor(clazz));
        System.out.println("  Old parameter size = " + oldParameterSize);
        System.out.println("  New parameter size = " + newParameterSize);
        System.out.println("  Max locals         = " + maxLocals);
      }

      // Create a variable map.
      int[] variableMap = new int[maxLocals];

      // Move unused parameters right after the parameter block.
      int usedParameterIndex = 0;
      int unusedParameterIndex = newParameterSize;
      for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++) {
        // Is the variable required as a parameter?
        if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
          // Keep the variable as a parameter.
          variableMap[parameterIndex] = usedParameterIndex++;
        } else {
          if (DEBUG) {
            System.out.println("  Deleting parameter #" + parameterIndex);
          }

          // Shift the variable to the unused parameter block,
          // in case it is still used as a variable.
          variableMap[parameterIndex] = unusedParameterIndex++;

          // Visit the method, if required.
          if (extraVariableMemberVisitor != null) {
            method.accept(clazz, extraVariableMemberVisitor);
          }
        }
      }

      // Fill out the remainder of the map.
      for (int variableIndex = oldParameterSize; variableIndex < maxLocals; variableIndex++) {
        variableMap[variableIndex] = variableIndex;
      }

      // Set the map.
      variableRemapper.setVariableMap(variableMap);

      // Remap the variables.
      variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
    }
  }
예제 #27
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;
  }
예제 #28
0
  /**
   * Writes the given configuration.
   *
   * @param configuration the configuration that is to be written out.
   * @throws IOException if an IO error occurs while writing the configuration.
   */
  public void write(Configuration configuration) throws IOException {
    // Write the program class path (input and output entries).
    writeJarOptions(
        ConfigurationConstants.INJARS_OPTION,
        ConfigurationConstants.OUTJARS_OPTION,
        configuration.programJars);
    writer.println();

    // Write the library class path (output entries only).
    writeJarOptions(
        ConfigurationConstants.LIBRARYJARS_OPTION,
        ConfigurationConstants.LIBRARYJARS_OPTION,
        configuration.libraryJars);
    writer.println();

    // Write the other options.
    writeOption(
        ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION,
        configuration.skipNonPublicLibraryClasses);
    writeOption(
        ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION,
        !configuration.skipNonPublicLibraryClassMembers);
    writeOption(ConfigurationConstants.KEEP_DIRECTORIES_OPTION, configuration.keepDirectories);
    writeOption(
        ConfigurationConstants.TARGET_OPTION,
        ClassUtil.externalClassVersion(configuration.targetClassVersion));
    writeOption(
        ConfigurationConstants.FORCE_PROCESSING_OPTION,
        configuration.lastModified == Long.MAX_VALUE);

    writeOption(ConfigurationConstants.DONT_SHRINK_OPTION, !configuration.shrink);
    writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage);

    writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION, !configuration.optimize);
    writeOption(ConfigurationConstants.OPTIMIZATIONS, configuration.optimizations);
    writeOption(ConfigurationConstants.OPTIMIZATION_PASSES, configuration.optimizationPasses);
    writeOption(
        ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION,
        configuration.allowAccessModification);
    writeOption(
        ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION,
        configuration.mergeInterfacesAggressively);

    writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION, !configuration.obfuscate);
    writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION, configuration.printMapping);
    writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION, configuration.applyMapping);
    writeOption(
        ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION, configuration.obfuscationDictionary);
    writeOption(
        ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION,
        configuration.classObfuscationDictionary);
    writeOption(
        ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION,
        configuration.packageObfuscationDictionary);
    writeOption(
        ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION, configuration.overloadAggressively);
    writeOption(
        ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION,
        configuration.useUniqueClassMemberNames);
    writeOption(
        ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION,
        !configuration.useMixedCaseClassNames);
    writeOption(
        ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION, configuration.keepPackageNames, true);
    writeOption(
        ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION,
        configuration.flattenPackageHierarchy,
        true);
    writeOption(
        ConfigurationConstants.REPACKAGE_CLASSES_OPTION, configuration.repackageClasses, true);
    writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION, configuration.keepAttributes);
    writeOption(
        ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION, configuration.keepParameterNames);
    writeOption(
        ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION,
        configuration.newSourceFileAttribute);
    writeOption(
        ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION, configuration.adaptClassStrings, true);
    writeOption(
        ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION,
        configuration.adaptResourceFileNames);
    writeOption(
        ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION,
        configuration.adaptResourceFileContents);

    writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify);
    writeOption(ConfigurationConstants.MICRO_EDITION_OPTION, configuration.microEdition);

    writeOption(ConfigurationConstants.VERBOSE_OPTION, configuration.verbose);
    writeOption(ConfigurationConstants.DONT_NOTE_OPTION, configuration.note, true);
    writeOption(ConfigurationConstants.DONT_WARN_OPTION, configuration.warn, true);
    writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION, configuration.ignoreWarnings);
    writeOption(
        ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration);
    writeOption(ConfigurationConstants.DUMP_OPTION, configuration.dump);

    writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION, configuration.printSeeds);
    writer.println();

    // Write the "why are you keeping" options.
    writeOptions(ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION, configuration.whyAreYouKeeping);

    // Write the keep options.
    writeOptions(KEEP_OPTIONS, configuration.keep);

    // Write the "no side effect methods" options.
    writeOptions(
        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects);

    if (writer.checkError()) {
      throw new IOException("Can't write configuration");
    }
  }
예제 #29
0
  /** 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);
  }