Package org.aspectj.org.eclipse.jdt.internal.compiler.classfmt

Source Code of org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader

/*******************************************************************************
* Copyright (c) 2000, 2007 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.aspectj.org.eclipse.jdt.internal.compiler.classfmt;

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

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

public class ClassFileReader extends ClassFileStruct implements IBinaryType {
private static String printTypeModifiers(int modifiers) {
  java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
  java.io.PrintWriter print = new java.io.PrintWriter(out);

  if ((modifiers & ClassFileConstants.AccPublic) != 0) print.print("public "); //$NON-NLS-1$
  if ((modifiers & ClassFileConstants.AccPrivate) != 0) print.print("private "); //$NON-NLS-1$
  if ((modifiers & ClassFileConstants.AccFinal) != 0) print.print("final "); //$NON-NLS-1$
  if ((modifiers & ClassFileConstants.AccSuper) != 0) print.print("super "); //$NON-NLS-1$
  if ((modifiers & ClassFileConstants.AccInterface) != 0) print.print("interface "); //$NON-NLS-1$
  if ((modifiers & ClassFileConstants.AccAbstract) != 0) print.print("abstract "); //$NON-NLS-1$
  print.flush();
  return out.toString();
}
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.aspectj.org.eclipse.jdt.internal.compiler.api.IBinaryField[]
*/
public IBinaryField[] getFields() {
  return this.fields;
}
/**
* @see org.aspectj.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;
}

// AspectJ start - original method has added boolean parameter, this new one has the original signature and simply
// passes in false.  This is all needed due to the support for inter type inner types
public IBinaryNestedType[] getMemberTypes() {
  return getMemberTypes(false);
}
// AspectJ end

/**
* 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.aspectj.org.eclipse.jdt.internal.compiler.api.IBinaryNestedType[]
*/
public IBinaryNestedType[] getMemberTypes(boolean keepIncorrectlyNamedInners) {
  // 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
        // AspectJ change:
        // was: && outerClassNameIdx == this.classNameIndex
        // now:
        && (keepIncorrectlyNamedInners || outerClassNameIdx == this.classNameIndex)
        // AspectJ end       
        && 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.aspectj.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;

  char[] name = getInnerSourceName(); // member or local scenario
  if (name == null) {
    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) {
      char[] newName = new char[name.length - start];
      System.arraycopy(name, start, newName, 0, newName.length);
      name = newName;
    }
  }
  return this.sourceName = name; 
}
/**
* 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: " + 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('}');
}
*/

// AspectJ Extension
public byte[] getReferenceBytes() {
  return reference;
}
// End AspectJ Extension
}
TOP

Related Classes of org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.