Logo Search packages:      
Sourcecode: ecj version File versions

ClassFileReader.java

/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.classfmt;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.util.Util;

public class ClassFileReader extends ClassFileStruct implements IBinaryType {
public static ClassFileReader read(InputStream stream, String fileName) throws ClassFormatException, IOException {
      return read(stream, fileName, false);
}
public static ClassFileReader read(File file) throws ClassFormatException, IOException {
      return read(file, false);
}
public static ClassFileReader read(InputStream stream, String fileName, boolean fullyInitialize) throws ClassFormatException, IOException {
      byte classFileBytes[] = Util.getInputStreamAsByteArray(stream, -1);
      ClassFileReader classFileReader = new ClassFileReader(classFileBytes, fileName.toCharArray());
      if (fullyInitialize) {
            classFileReader.initialize();
      }
      return classFileReader;
}
public static ClassFileReader read(File file, boolean fullyInitialize) throws ClassFormatException, IOException {
      byte classFileBytes[] = Util.getFileByteContent(file);
      ClassFileReader classFileReader = new ClassFileReader(classFileBytes, file.getAbsolutePath().toCharArray());
      if (fullyInitialize) {
            classFileReader.initialize();
      }
      return classFileReader;
}
public static ClassFileReader read(
      java.util.zip.ZipFile zip, 
      String filename)
      throws ClassFormatException, java.io.IOException {
            return read(zip, filename, false);
}
public static ClassFileReader read(
      java.util.zip.ZipFile zip, 
      String filename,
      boolean fullyInitialize)
      throws ClassFormatException, java.io.IOException {
      java.util.zip.ZipEntry ze = zip.getEntry(filename);
      if (ze == null)
            return null;
      byte classFileBytes[] = Util.getZipEntryByteContent(ze, zip);
      ClassFileReader classFileReader = new ClassFileReader(classFileBytes, filename.toCharArray());
      if (fullyInitialize) {
            classFileReader.initialize();
      }
      return classFileReader;
}
public static ClassFileReader read(String fileName) throws ClassFormatException, java.io.IOException {
      return read(fileName, false);
}
public static ClassFileReader read(String fileName, boolean fullyInitialize) throws ClassFormatException, java.io.IOException {
      return read(new File(fileName), fullyInitialize);
}
      private int accessFlags;
      private char[] classFileName;
      private char[] className;
      private int classNameIndex;
      private int constantPoolCount;
      private AnnotationInfo[] annotations;
      private FieldInfo[] fields;
      private int fieldsCount;
      // initialized in case the .class file is a nested type
      private InnerClassInfo innerInfo;
      private int innerInfoIndex;
      private InnerClassInfo[] innerInfos;
      private char[][] interfaceNames;
      private int interfacesCount;
      private MethodInfo[] methods;
      private int methodsCount;
      private char[] signature;
      private char[] sourceName;
      private char[] sourceFileName;
      private char[] superclassName;
      private long tagBits;
      private long version;
      
      private char[] enclosingTypeName;

/**
 * @param classFileBytes Actual bytes of a .class file
 * @param fileName      Actual name of the file that contains the bytes, can be null
 * 
 * @exception ClassFormatException
 */
public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
      this(classFileBytes, fileName, false);
}

/**
 * @param classFileBytes byte[]
 *          Actual bytes of a .class file
 * 
 * @param fileName char[]
 *          Actual name of the file that contains the bytes, can be null
 * 
 * @param fullyInitialize boolean
 *          Flag to fully initialize the new object
 * @exception ClassFormatException
 */
public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
      // This method looks ugly but is actually quite simple, the constantPool is constructed
      // in 3 passes.  All non-primitive constant pool members that usually refer to other members
      // by index are tweaked to have their value in inst vars, this minor cost at read-time makes
      // all subsequent uses of the constant pool element faster.
      super(classFileBytes, null, 0);
      this.classFileName = fileName;
      int readOffset = 10;
      try {
            this.version = ((long)this.u2At(6) << 16) + this.u2At(4); // major<<16 + minor
            constantPoolCount = this.u2At(8);
            // Pass #1 - Fill in all primitive constants
            this.constantPoolOffsets = new int[constantPoolCount];
            for (int i = 1; i < constantPoolCount; i++) {
                  int tag = this.u1At(readOffset);
                  switch (tag) {
                        case ClassFileConstants.Utf8Tag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += u2At(readOffset + 1);
                              readOffset += ClassFileConstants.ConstantUtf8FixedSize;
                              break;
                        case ClassFileConstants.IntegerTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantIntegerFixedSize;
                              break;
                        case ClassFileConstants.FloatTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantFloatFixedSize;
                              break;
                        case ClassFileConstants.LongTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantLongFixedSize;
                              i++;
                              break;
                        case ClassFileConstants.DoubleTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantDoubleFixedSize;
                              i++;
                              break;
                        case ClassFileConstants.ClassTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantClassFixedSize;
                              break;
                        case ClassFileConstants.StringTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantStringFixedSize;
                              break;
                        case ClassFileConstants.FieldRefTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantFieldRefFixedSize;
                              break;
                        case ClassFileConstants.MethodRefTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantMethodRefFixedSize;
                              break;
                        case ClassFileConstants.InterfaceMethodRefTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantInterfaceMethodRefFixedSize;
                              break;
                        case ClassFileConstants.NameAndTypeTag :
                              this.constantPoolOffsets[i] = readOffset;
                              readOffset += ClassFileConstants.ConstantNameAndTypeFixedSize;
                  }
            }
            // Read and validate access flags
            this.accessFlags = u2At(readOffset);
            readOffset += 2;

            // Read the classname, use exception handlers to catch bad format
            this.classNameIndex = u2At(readOffset);
            this.className = getConstantClassNameAt(this.classNameIndex);
            readOffset += 2;

            // Read the superclass name, can be null for java.lang.Object
            int superclassNameIndex = u2At(readOffset);
            readOffset += 2;
            // if superclassNameIndex is equals to 0 there is no need to set a value for the 
            // field this.superclassName. null is fine.
            if (superclassNameIndex != 0) {
                  this.superclassName = getConstantClassNameAt(superclassNameIndex);
            }

            // Read the interfaces, use exception handlers to catch bad format
            this.interfacesCount = u2At(readOffset);
            readOffset += 2;
            if (this.interfacesCount != 0) {
                  this.interfaceNames = new char[this.interfacesCount][];
                  for (int i = 0; i < this.interfacesCount; i++) {
                        this.interfaceNames[i] = getConstantClassNameAt(u2At(readOffset));
                        readOffset += 2;
                  }
            }
            // Read the fields, use exception handlers to catch bad format
            this.fieldsCount = u2At(readOffset);
            readOffset += 2;
            if (this.fieldsCount != 0) {
                  FieldInfo field;
                  this.fields = new FieldInfo[this.fieldsCount];
                  for (int i = 0; i < this.fieldsCount; i++) {
                        field = FieldInfo.createField(reference, this.constantPoolOffsets, readOffset);
                        this.fields[i] = field;
                        readOffset += field.sizeInBytes();
                  }
            }
            // Read the methods
            this.methodsCount = u2At(readOffset);
            readOffset += 2;
            if (this.methodsCount != 0) {
                  this.methods = new MethodInfo[this.methodsCount];
                  boolean isAnnotationType = (this.accessFlags & ClassFileConstants.AccAnnotation) != 0;
                  for (int i = 0; i < this.methodsCount; i++) {
                        this.methods[i] = isAnnotationType
                              ? AnnotationMethodInfo.createAnnotationMethod(reference, this.constantPoolOffsets, readOffset)
                              : MethodInfo.createMethod(reference, this.constantPoolOffsets, readOffset);
                        readOffset += this.methods[i].sizeInBytes();
                  }
            }

            // Read the attributes
            int attributesCount = u2At(readOffset);
            readOffset += 2;

            for (int i = 0; i < attributesCount; i++) {
                  int utf8Offset = this.constantPoolOffsets[u2At(readOffset)];
                  char[] attributeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
                  if (attributeName.length == 0) {
                        readOffset += (6 + u4At(readOffset + 2));
                        continue;
                  }
                  switch(attributeName[0] ) {
                        case 'E' :
                              if (CharOperation.equals(attributeName, AttributeNamesConstants.EnclosingMethodName)) {
                                    utf8Offset = 
                                          constantPoolOffsets[u2At(constantPoolOffsets[u2At(readOffset + 6)] - structOffset + 1)] - structOffset; 
                                    this.enclosingTypeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
                              }
                              break;
                        case 'D' :
                              if (CharOperation.equals(attributeName, AttributeNamesConstants.DeprecatedName)) {
                                    this.accessFlags |= ClassFileConstants.AccDeprecated;
                              }
                              break;
                        case 'I' :
                              if (CharOperation.equals(attributeName, AttributeNamesConstants.InnerClassName)) {
                                    int innerOffset = readOffset + 6;
                                    int number_of_classes = u2At(innerOffset);
                                    if (number_of_classes != 0) {
                                          innerOffset+= 2;
                                          this.innerInfos = new InnerClassInfo[number_of_classes];
                                          for (int j = 0; j < number_of_classes; j++) {
                                                this.innerInfos[j] = 
                                                      new InnerClassInfo(reference, this.constantPoolOffsets, innerOffset); 
                                                if (this.classNameIndex == this.innerInfos[j].innerClassNameIndex) {
                                                      this.innerInfo = this.innerInfos[j];
                                                      this.innerInfoIndex = j;
                                                }
                                                innerOffset += 8;
                                          }
                                          if (this.innerInfo != null) {
                                                char[] enclosingType = this.innerInfo.getEnclosingTypeName();
                                                if (enclosingType != null) {
                                                      this.enclosingTypeName = enclosingType;
                                                }
                                          }
                                    }
                              } else if (CharOperation.equals(attributeName, AttributeNamesConstants.InconsistentHierarchy)) {
                                    this.tagBits |= TagBits.HasInconsistentHierarchy;
                              }
                              break;
                        case 'S' :
                              if (attributeName.length > 2) {
                                    switch(attributeName[1]) {
                                          case 'o' :
                                                if (CharOperation.equals(attributeName, AttributeNamesConstants.SourceName)) {
                                                      utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
                                                      this.sourceFileName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
                                                }
                                                break;
                                          case 'y' :
                                                if (CharOperation.equals(attributeName, AttributeNamesConstants.SyntheticName)) {
                                                      this.accessFlags |= ClassFileConstants.AccSynthetic;
                                                }
                                                break;
                                          case 'i' :
                                                if (CharOperation.equals(attributeName, AttributeNamesConstants.SignatureName)) {
                                                      utf8Offset = this.constantPoolOffsets[u2At(readOffset + 6)];
                                                      this.signature = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));                      
                                                }
                                    }
                              }
                              break;
                        case 'R' :
                              if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeVisibleAnnotationsName)) {
                                    decodeAnnotations(readOffset, true);
                              } else if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeInvisibleAnnotationsName)) {
                                    decodeAnnotations(readOffset, false);
                              }
                              break;
                  }
                  readOffset += (6 + u4At(readOffset + 2));
            }
            if (fullyInitialize) {
                  this.initialize();
            }
      } catch(ClassFormatException e) {
            throw e;
      } catch (Exception e) {
            throw new ClassFormatException(
                  ClassFormatException.ErrTruncatedInput, 
                  readOffset); 
      }
}

/**
 *    Answer the receiver's access flags.  The value of the access_flags
 *    item is a mask of modifiers used with class and interface declarations.
 *  @return int 
 */
public int accessFlags() {
      return this.accessFlags;
}
private void decodeAnnotations(int offset, boolean runtimeVisible) {
      int numberOfAnnotations = u2At(offset + 6);
      if (numberOfAnnotations > 0) {
            int readOffset = offset + 8;
            AnnotationInfo[] newInfos = null;
            int newInfoCount = 0;
            for (int i = 0; i < numberOfAnnotations; i++) {
                  // With the last parameter being 'false', the data structure will not be flushed out
                  AnnotationInfo newInfo = new AnnotationInfo(this.reference, this.constantPoolOffsets, readOffset, runtimeVisible, false);
                  readOffset += newInfo.readOffset;
                  long standardTagBits = newInfo.standardAnnotationTagBits;
                  if (standardTagBits != 0) {
                        this.tagBits |= standardTagBits;
                  } else {
                        if (newInfos == null)
                              newInfos = new AnnotationInfo[numberOfAnnotations - i];
                        newInfos[newInfoCount++] = newInfo;
                  }
            }
            if (newInfos == null)
                  return; // nothing to record in this.annotations

            if (this.annotations == null) {
                  if (newInfoCount != newInfos.length)
                        System.arraycopy(newInfos, 0, newInfos = new AnnotationInfo[newInfoCount], 0, newInfoCount);
                  this.annotations = newInfos;
            } else {
                  int length = this.annotations.length;
                  AnnotationInfo[] temp = new AnnotationInfo[length + newInfoCount];
                  System.arraycopy(this.annotations, 0, temp, 0, length);
                  System.arraycopy(newInfos, 0, temp, length, newInfoCount);
                  this.annotations = temp;
            }
      }
}
/**
 * Answer the char array that corresponds to the class name of the constant class.
 * constantPoolIndex is the index in the constant pool that is a constant class entry.
 *
 * @param constantPoolIndex int
 * @return char[]
 */
private char[] getConstantClassNameAt(int constantPoolIndex) {
      int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
      return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
}
/**
 * Answer the int array that corresponds to all the offsets of each entry in the constant pool
 *
 * @return int[]
 */
public int[] getConstantPoolOffsets() {
      return this.constantPoolOffsets;
}
/*
 * Answer the resolved compoundName of the enclosing type
 * or null if the receiver is a top level type.
 */
public char[] getEnclosingTypeName() {
      return this.enclosingTypeName;
}
/**
 * Answer the receiver's this.fields or null if the array is empty.
 * @return org.eclipse.jdt.internal.compiler.api.IBinaryField[]
 */
public IBinaryField[] getFields() {
      return this.fields;
}
/**
 * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
 */
public char[] getFileName() {
      return this.classFileName;
}
public char[] getGenericSignature() {
      return this.signature;
}
/**
 * Answer the source name if the receiver is a inner type. Return null if it is an anonymous class or if the receiver is a top-level class.
 * e.g.
 * public class A {
 *    public class B {
 *    }
 *    public void foo() {
 *          class C {}
 *    }
 *    public Runnable bar() {
 *          return new Runnable() {
 *                public void run() {}
 *          };
 *    }
 * }
 * It returns {'B'} for the member A$B
 * It returns null for A
 * It returns {'C'} for the local class A$1$C
 * It returns null for the anonymous A$1
 * @return char[]
 */
public char[] getInnerSourceName() {
      if (this.innerInfo != null)
            return this.innerInfo.getSourceName();
      return null;
}
/**
 * Answer the resolved names of the receiver's interfaces in the
 * class file format as specified in section 4.2 of the Java 2 VM spec
 * or null if the array is empty.
 *
 * For example, java.lang.String is java/lang/String.
 * @return char[][]
 */
public char[][] getInterfaceNames() {
      return this.interfaceNames;
}

/**
 * Answer the receiver's nested types or null if the array is empty.
 * 
 * This nested type info is extracted from the inner class attributes. Ask the
 * name environment to find a member type using its compound name
 * 
 * @return org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
 */
public IBinaryNestedType[] getMemberTypes() {
      // we might have some member types of the current type
      if (this.innerInfos == null) return null;

      int length = this.innerInfos.length;
      int startingIndex = this.innerInfo != null ? this.innerInfoIndex + 1 : 0;
      if (length != startingIndex) {
            IBinaryNestedType[] memberTypes = 
                  new IBinaryNestedType[length - this.innerInfoIndex]; 
            int memberTypeIndex = 0;
            for (int i = startingIndex; i < length; i++) {
                  InnerClassInfo currentInnerInfo = this.innerInfos[i];
                  int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
                  int innerNameIndex = currentInnerInfo.innerNameIndex;
                  /*
                   * Checking that outerClassNameIDx is different from 0 should be enough to determine if an inner class
                   * attribute entry is a member class, but due to the bug:
                   * http://dev.eclipse.org/bugs/show_bug.cgi?id=14592
                   * we needed to add an extra check. So we check that innerNameIndex is different from 0 as well.
                   * 
                   * https://bugs.eclipse.org/bugs/show_bug.cgi?id=49879
                   * From JavaMail 1.2, the class javax.mail.Folder contains an anonymous class in the
                   * terminateQueue() method for which the inner attribute is boggus.
                   * outerClassNameIdx is not 0, innerNameIndex is not 0, but the sourceName length is 0.
                   * So I added this extra check to filter out this anonymous class from the 
                   * member types.
                   */
                  if (outerClassNameIdx != 0
                        && innerNameIndex != 0
                        && outerClassNameIdx == this.classNameIndex
                        && currentInnerInfo.getSourceName().length != 0) {
                        memberTypes[memberTypeIndex++] = currentInnerInfo;
                  }
            }
            if (memberTypeIndex == 0) return null;
            if (memberTypeIndex != memberTypes.length) {
                  // we need to resize the memberTypes array. Some local or anonymous classes
                  // are present in the current class.
                  System.arraycopy(
                        memberTypes, 
                        0, 
                        (memberTypes = new IBinaryNestedType[memberTypeIndex]), 
                        0, 
                        memberTypeIndex); 
            }
            return memberTypes;
      }
      return null;
}
/**
 * Answer the receiver's this.methods or null if the array is empty.
 * @return org.eclipse.jdt.internal.compiler.api.env.IBinaryMethod[]
 */
public IBinaryMethod[] getMethods() {
      return this.methods;
}
/**
 * @return the annotations or null if there is none.
 */
public IBinaryAnnotation[] getAnnotations() {
      return this.annotations;
}
/**
 * Answer an int whose bits are set according the access constants
 * defined by the VM spec.
 * Set the AccDeprecated and AccSynthetic bits if necessary
 * @return int
 */
public int getModifiers() {
      if (this.innerInfo != null) {
            return this.innerInfo.getModifiers() | (this.accessFlags & ClassFileConstants.AccDeprecated);
      }
      return this.accessFlags;
}
/**
 * Answer the resolved name of the type in the
 * class file format as specified in section 4.2 of the Java 2 VM spec.
 *
 * For example, java.lang.String is java/lang/String.
 * @return char[]
 */
public char[] getName() {
      return this.className;
}
public char[] getSourceName() {
      if (this.sourceName != null) 
            return this.sourceName;
      
      this.sourceName = getInnerSourceName(); // member or local scenario
      if (this.sourceName == null) {
            char[] name = getName(); // extract from full name
            int start;
            if (isAnonymous()) {
                  start = CharOperation.indexOf('$', name, CharOperation.lastIndexOf('/', name) + 1) + 1;               
            } else {
                  start = CharOperation.lastIndexOf('/', name) + 1;                 
            }
            if (start == 0) {
                  this.sourceName = name;
            } else {
                  this.sourceName = new char[name.length - start];
                  System.arraycopy(name, start, this.sourceName, 0, this.sourceName.length);
            }     
      }
      return this.sourceName; 
}
/**
 * Answer the resolved name of the receiver's superclass in the
 * class file format as specified in section 4.2 of the Java 2 VM spec
 * or null if it does not have one.
 *
 * For example, java.lang.String is java/lang/String.
 * @return char[]
 */
public char[] getSuperclassName() {
      return this.superclassName;
}
public long getTagBits() {
      return this.tagBits;
}
/**
 * Answer the major/minor version defined in this class file according to the VM spec.
 * as a long: (major<<16)+minor
 * @return the major/minor version found
 */
public long getVersion() {
      return this.version;
}
private boolean hasNonSyntheticFieldChanges(FieldInfo[] currentFieldInfos, FieldInfo[] otherFieldInfos) {
      int length1 = currentFieldInfos == null ? 0 : currentFieldInfos.length;
      int length2 = otherFieldInfos == null ? 0 : otherFieldInfos.length;
      int index1 = 0;
      int index2 = 0;

      end : while (index1 < length1 && index2 < length2) {
            while (currentFieldInfos[index1].isSynthetic()) {
                  if (++index1 >= length1) break end;
            }
            while (otherFieldInfos[index2].isSynthetic()) {
                  if (++index2 >= length2) break end;
            }
            if (hasStructuralFieldChanges(currentFieldInfos[index1++], otherFieldInfos[index2++]))
                  return true;
      }

      while (index1 < length1) {
            if (!currentFieldInfos[index1++].isSynthetic()) return true;
      }
      while (index2 < length2) {
            if (!otherFieldInfos[index2++].isSynthetic()) return true;
      }
      return false;
}
private boolean hasNonSyntheticMethodChanges(MethodInfo[] currentMethodInfos, MethodInfo[] otherMethodInfos) {
      int length1 = currentMethodInfos == null ? 0 : currentMethodInfos.length;
      int length2 = otherMethodInfos == null ? 0 : otherMethodInfos.length;
      int index1 = 0;
      int index2 = 0;

      MethodInfo m;
      end : while (index1 < length1 && index2 < length2) {
            while ((m = currentMethodInfos[index1]).isSynthetic() || m.isClinit()) {
                  if (++index1 >= length1) break end;
            }
            while ((m = otherMethodInfos[index2]).isSynthetic() || m.isClinit()) {
                  if (++index2 >= length2) break end;
            }
            if (hasStructuralMethodChanges(currentMethodInfos[index1++], otherMethodInfos[index2++]))
                  return true;
      }

      while (index1 < length1) {
            if (!((m = currentMethodInfos[index1++]).isSynthetic() || m.isClinit())) return true;
      }
      while (index2 < length2) {
            if (!((m = otherMethodInfos[index2++]).isSynthetic() || m.isClinit())) return true;
      }
      return false;
}
/**
 * Check if the receiver has structural changes compare to the byte array in argument.
 * Structural changes are:
 * - modifiers changes for the class, the this.fields or the this.methods
 * - signature changes for this.fields or this.methods.
 * - changes in the number of this.fields or this.methods
 * - changes for field constants
 * - changes for thrown exceptions
 * - change for the super class or any super interfaces.
 * - changes for member types name or modifiers
 * If any of these changes occurs, the method returns true. false otherwise. 
 * The synthetic fields are included and the members are not required to be sorted.
 * @param newBytes the bytes of the .class file we want to compare the receiver to
 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
 */
public boolean hasStructuralChanges(byte[] newBytes) {
      return hasStructuralChanges(newBytes, true, true);
}
/**
 * Check if the receiver has structural changes compare to the byte array in argument.
 * Structural changes are:
 * - modifiers changes for the class, the this.fields or the this.methods
 * - signature changes for this.fields or this.methods.
 * - changes in the number of this.fields or this.methods
 * - changes for field constants
 * - changes for thrown exceptions
 * - change for the super class or any super interfaces.
 * - changes for member types name or modifiers
 * If any of these changes occurs, the method returns true. false otherwise.
 * @param newBytes the bytes of the .class file we want to compare the receiver to
 * @param orderRequired a boolean indicating whether the members should be sorted or not
 * @param excludesSynthetic a boolean indicating whether the synthetic members should be used in the comparison
 * @return boolean Returns true is there is a structural change between the two .class files, false otherwise
 */
public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
      try {
            ClassFileReader newClassFile =
                  new ClassFileReader(newBytes, this.classFileName);
            // type level comparison
            // modifiers
            if (this.getModifiers() != newClassFile.getModifiers())
                  return true;

            // only consider a portion of the tagbits which indicate a structural change for dependents
            // e.g. @Override change has no influence outside
            long OnlyStructuralTagBits = TagBits.AnnotationTargetMASK // different @Target status ?
                  | TagBits.AnnotationDeprecated // different @Deprecated status ?
                  | TagBits.AnnotationRetentionMASK // different @Retention status ?
                  | TagBits.HasInconsistentHierarchy; // different hierarchy status ?
            
            // meta-annotations
            if ((this.getTagBits() & OnlyStructuralTagBits) != (newClassFile.getTagBits() & OnlyStructuralTagBits))
                  return true;
            
            // generic signature
            if (!CharOperation.equals(this.getGenericSignature(), newClassFile.getGenericSignature()))
                  return true;
            // superclass
            if (!CharOperation.equals(this.getSuperclassName(), newClassFile.getSuperclassName()))
                  return true;
            // interfaces
            char[][] newInterfacesNames = newClassFile.getInterfaceNames();
            if (this.interfaceNames != newInterfacesNames) { // TypeConstants.NoSuperInterfaces
                  int newInterfacesLength = newInterfacesNames == null ? 0 : newInterfacesNames.length;
                  if (newInterfacesLength != this.interfacesCount)
                        return true;
                  for (int i = 0, max = this.interfacesCount; i < max; i++)
                        if (!CharOperation.equals(this.interfaceNames[i], newInterfacesNames[i]))
                              return true;
            }

            // member types
            IBinaryNestedType[] currentMemberTypes = this.getMemberTypes();
            IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
            if (currentMemberTypes != otherMemberTypes) { // TypeConstants.NoMemberTypes
                  int currentMemberTypeLength = currentMemberTypes == null ? 0 : currentMemberTypes.length;
                  int otherMemberTypeLength = otherMemberTypes == null ? 0 : otherMemberTypes.length;
                  if (currentMemberTypeLength != otherMemberTypeLength)
                        return true;
                  for (int i = 0; i < currentMemberTypeLength; i++)
                        if (!CharOperation.equals(currentMemberTypes[i].getName(), otherMemberTypes[i].getName())
                              || currentMemberTypes[i].getModifiers() != otherMemberTypes[i].getModifiers())
                                    return true;
            }

            // fields
            FieldInfo[] otherFieldInfos = (FieldInfo[]) newClassFile.getFields();
            int otherFieldInfosLength = otherFieldInfos == null ? 0 : otherFieldInfos.length;
            boolean compareFields = true;
            if (this.fieldsCount == otherFieldInfosLength) {
                  int i = 0;
                  for (; i < this.fieldsCount; i++)
                        if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i])) break;
                  if ((compareFields = i != this.fieldsCount) && !orderRequired && !excludesSynthetic)
                        return true;
            }
            if (compareFields) {
                  if (this.fieldsCount != otherFieldInfosLength && !excludesSynthetic)
                        return true;
                  if (orderRequired) {
                        if (this.fieldsCount != 0)
                              Arrays.sort(this.fields);
                        if (otherFieldInfosLength != 0)
                              Arrays.sort(otherFieldInfos);
                  }
                  if (excludesSynthetic) {
                        if (hasNonSyntheticFieldChanges(this.fields, otherFieldInfos))
                              return true;
                  } else {
                        for (int i = 0; i < this.fieldsCount; i++)
                              if (hasStructuralFieldChanges(this.fields[i], otherFieldInfos[i]))
                                    return true;
                  }
            }
            
            // methods
            MethodInfo[] otherMethodInfos = (MethodInfo[]) newClassFile.getMethods();
            int otherMethodInfosLength = otherMethodInfos == null ? 0 : otherMethodInfos.length;
            boolean compareMethods = true;
            if (this.methodsCount == otherMethodInfosLength) {
                  int i = 0;
                  for (; i < this.methodsCount; i++)
                        if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i])) break;
                  if ((compareMethods = i != this.methodsCount) && !orderRequired && !excludesSynthetic)
                        return true;
            }
            if (compareMethods) {
                  if (this.methodsCount != otherMethodInfosLength && !excludesSynthetic)
                        return true;
                  if (orderRequired) {
                        if (this.methodsCount != 0)
                              Arrays.sort(this.methods);
                        if (otherMethodInfosLength != 0)
                              Arrays.sort(otherMethodInfos);      
                  }
                  if (excludesSynthetic) {
                        if (hasNonSyntheticMethodChanges(this.methods, otherMethodInfos))
                              return true;
                  } else {
                        for (int i = 0; i < this.methodsCount; i++)
                              if (hasStructuralMethodChanges(this.methods[i], otherMethodInfos[i]))
                                    return true;
                  }
            }

            return false;
      } catch (ClassFormatException e) {
            return true;
      }
}
private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
      // generic signature
      if (!CharOperation.equals(currentFieldInfo.getGenericSignature(), otherFieldInfo.getGenericSignature()))
            return true;
      if (currentFieldInfo.getModifiers() != otherFieldInfo.getModifiers())
            return true;
      if ((currentFieldInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherFieldInfo.getTagBits() & TagBits.AnnotationDeprecated))
            return true;
      if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
            return true;
      if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
            return true;
      if (currentFieldInfo.hasConstant() != otherFieldInfo.hasConstant())
            return true;
      if (currentFieldInfo.hasConstant()) {
            Constant currentConstant = currentFieldInfo.getConstant();
            Constant otherConstant = otherFieldInfo.getConstant();
            if (currentConstant.typeID() != otherConstant.typeID())
                  return true;
            if (!currentConstant.getClass().equals(otherConstant.getClass()))
                  return true;
            switch (currentConstant.typeID()) {
                  case TypeIds.T_int :
                        return currentConstant.intValue() != otherConstant.intValue();
                  case TypeIds.T_byte :
                        return currentConstant.byteValue() != otherConstant.byteValue();
                  case TypeIds.T_short :
                        return currentConstant.shortValue() != otherConstant.shortValue();
                  case TypeIds.T_char :
                        return currentConstant.charValue() != otherConstant.charValue();
                  case TypeIds.T_long :
                        return currentConstant.longValue() != otherConstant.longValue();
                  case TypeIds.T_float :
                        return currentConstant.floatValue() != otherConstant.floatValue();
                  case TypeIds.T_double :
                        return currentConstant.doubleValue() != otherConstant.doubleValue();
                  case TypeIds.T_boolean :
                        return currentConstant.booleanValue() != otherConstant.booleanValue();
                  case TypeIds.T_JavaLangString :
                        return !currentConstant.stringValue().equals(otherConstant.stringValue());
            }
      }
      return false;
}
private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodInfo otherMethodInfo) {
      // generic signature
      if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
            return true;
      if (currentMethodInfo.getModifiers() != otherMethodInfo.getModifiers())
            return true;
      if ((currentMethodInfo.getTagBits() & TagBits.AnnotationDeprecated) != (otherMethodInfo.getTagBits() & TagBits.AnnotationDeprecated))
            return true;
      if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
            return true;
      if (!CharOperation.equals(currentMethodInfo.getMethodDescriptor(), otherMethodInfo.getMethodDescriptor()))
            return true;
      if (!CharOperation.equals(currentMethodInfo.getGenericSignature(), otherMethodInfo.getGenericSignature()))
            return true;

      char[][] currentThrownExceptions = currentMethodInfo.getExceptionTypeNames();
      char[][] otherThrownExceptions = otherMethodInfo.getExceptionTypeNames();
      if (currentThrownExceptions != otherThrownExceptions) { // TypeConstants.NoExceptions
            int currentThrownExceptionsLength = currentThrownExceptions == null ? 0 : currentThrownExceptions.length;
            int otherThrownExceptionsLength = otherThrownExceptions == null ? 0 : otherThrownExceptions.length;
            if (currentThrownExceptionsLength != otherThrownExceptionsLength)
                  return true;
            for (int k = 0; k < currentThrownExceptionsLength; k++)
                  if (!CharOperation.equals(currentThrownExceptions[k], otherThrownExceptions[k]))
                        return true;
      }
      return false;
}
/**
 * This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
 * will be therefore fully initialized and we can get rid of the bytes.
 */
private void initialize() throws ClassFormatException {
      try {
            for (int i = 0, max = fieldsCount; i < max; i++) {
                  fields[i].initialize();
            }
            for (int i = 0, max = methodsCount; i < max; i++) {
                  methods[i].initialize();
            }
            if (innerInfos != null) {
                  for (int i = 0, max = innerInfos.length; i < max; i++) {
                        innerInfos[i].initialize();
                  }
            }
            if (annotations != null) {
                  for (int i = 0, max = annotations.length; i < max; i++) {
                        annotations[i].initialize();
                  }
            }
            this.reset();
      } catch(RuntimeException e) {
            ClassFormatException exception = new ClassFormatException(e, this.classFileName);
            throw exception;
      }
}
/**
 * Answer true if the receiver is an anonymous type, false otherwise
 *
 * @return <CODE>boolean</CODE>
 */
public boolean isAnonymous() {
      if (this.innerInfo == null) return false;
      char[] innerSourceName = this.innerInfo.getSourceName();
      return (innerSourceName == null || innerSourceName.length == 0);
}
/**
 * Answer whether the receiver contains the resolved binary form
 * or the unresolved source form of the type.
 * @return boolean
 */
public boolean isBinaryType() {
      return true;
}

/**
 * Answer true if the receiver is a local type, false otherwise
 *
 * @return <CODE>boolean</CODE>
 */
public boolean isLocal() {
      if (this.innerInfo == null) return false;
      if (this.innerInfo.getEnclosingTypeName() != null) return false;
      char[] innerSourceName = this.innerInfo.getSourceName();
      return (innerSourceName != null && innerSourceName.length > 0);   
}
/**
 * Answer true if the receiver is a member type, false otherwise
 *
 * @return <CODE>boolean</CODE>
 */
public boolean isMember() {
      if (this.innerInfo == null) return false;
      if (this.innerInfo.getEnclosingTypeName() == null) return false;
      char[] innerSourceName = this.innerInfo.getSourceName();
      return (innerSourceName != null && innerSourceName.length > 0);    // protection against ill-formed attributes (67600)
}
/**
 * Answer true if the receiver is a nested type, false otherwise
 *
 * @return <CODE>boolean</CODE>
 */
public boolean isNestedType() {
      return this.innerInfo != null;
}
/**
 * Answer the source file name attribute. Return null if there is no source file attribute for the receiver.
 * 
 * @return char[]
 */
public char[] sourceFileName() {
      return this.sourceFileName;
}
public String toString() {
      java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
      java.io.PrintWriter print = new java.io.PrintWriter(out);
      print.println(this.getClass().getName() + "{"); //$NON-NLS-1$
      print.println(" this.className: " + new String(getName())); //$NON-NLS-1$
      print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName()))); //$NON-NLS-2$ //$NON-NLS-1$
      print.println(" access_flags: " + ClassFileStruct.printTypeModifiers(this.accessFlags()) + "(" + this.accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
      print.flush();
      return out.toString();
}

/*
public static void main(String[] args) throws ClassFormatException, IOException {
      if (args == null || args.length != 1) {
            System.err.println("ClassFileReader <filename>"); //$NON-NLS-1$
            System.exit(1);
      }
      File file = new File(args[0]);
      ClassFileReader reader = read(file, true);
      if (reader.annotations != null) {
            System.err.println();
            for (int i = 0; i < reader.annotations.length; i++)
                  System.err.println(reader.annotations[i]);
      }
      System.err.print("class "); //$NON-NLS-1$
      System.err.print(reader.getName());
      char[] superclass = reader.getSuperclassName();
      if (superclass != null) {
            System.err.print(" extends "); //$NON-NLS-1$
            System.err.print(superclass);
      }
      System.err.println();
      char[][] interfaces = reader.getInterfaceNames();
      if (interfaces != null && interfaces.length > 0) {
            System.err.print(" implements "); //$NON-NLS-1$
            for (int i = 0; i < interfaces.length; i++) {
                  if (i != 0) System.err.print(", "); //$NON-NLS-1$           
                  System.err.println(interfaces[i]);
            }
      }
      System.err.println();
      System.err.println('{');
      if (reader.fields != null) {
            for (int i = 0; i < reader.fields.length; i++) {
                  System.err.println(reader.fields[i]);
                  System.err.println();
            }
      }
      if (reader.methods != null) {
            for (int i = 0; i < reader.methods.length; i++) {
                  System.err.println(reader.methods[i]);
                  System.err.println();
            }
      }
      System.err.println();
      System.err.println('}');
}
*/
}

Generated by  Doxygen 1.6.0   Back to index