org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
This is Eclipse JDT Core Batch Compiler used by Scout SDK
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2015 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
* Stephan Herrmann - Contribution for
* Bug 365992 - [builder] [null] Change of nullness for a parameter doesn't trigger a build for the files that call the method
* Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
* Bug 440687 - [compiler][batch][null] improve command line option for external annotations
* Andy Clement (GoPivotal, Inc) [email protected] - Contributions for
* bug 407191 - [1.8] Binary access support for type annotations
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.classfmt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
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.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.util.Util;
public class ClassFileReader extends ClassFileStruct implements IBinaryType {
private int accessFlags;
private char[] classFileName;
private char[] className;
private int classNameIndex;
private int constantPoolCount;
private AnnotationInfo[] annotations;
private TypeAnnotationInfo[] typeAnnotations;
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;
private char[][][] missingTypeNames;
private int enclosingNameAndTypeIndex;
private char[] enclosingMethod;
private ExternalAnnotationProvider annotationProvider;
private ExternalAnnotationStatus externalAnnotationStatus = ExternalAnnotationStatus.NOT_EEA_CONFIGURED;
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(File file) throws ClassFormatException, IOException {
return read(file, false);
}
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(InputStream stream, String fileName) throws ClassFormatException, IOException {
return read(stream, fileName, 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(
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);
}
/**
* @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)u2At(6) << 16) + u2At(4); // major<<16 + minor
this.constantPoolCount = u2At(8);
// Pass #1 - Fill in all primitive constants
this.constantPoolOffsets = new int[this.constantPoolCount];
for (int i = 1; i < this.constantPoolCount; i++) {
int tag = 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;
break;
case ClassFileConstants.MethodHandleTag :
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantMethodHandleFixedSize;
break;
case ClassFileConstants.MethodTypeTag :
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantMethodTypeFixedSize;
break;
case ClassFileConstants.InvokeDynamicTag :
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantInvokeDynamicFixedSize;
break;
}
}
// 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(this.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(this.reference, this.constantPoolOffsets, readOffset)
: MethodInfo.createMethod(this.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 =
this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(readOffset + 6)] + 1)];
this.enclosingTypeName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
this.enclosingNameAndTypeIndex = u2At(readOffset + 8);
}
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(this.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.HierarchyHasProblems;
}
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);
} else if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeVisibleTypeAnnotationsName)) {
decodeTypeAnnotations(readOffset, true);
} else if (CharOperation.equals(attributeName, AttributeNamesConstants.RuntimeInvisibleTypeAnnotationsName)) {
decodeTypeAnnotations(readOffset, false);
}
break;
case 'M' :
if (CharOperation.equals(attributeName, AttributeNamesConstants.MissingTypesName)) {
// decode the missing types
int missingTypeOffset = readOffset + 6;
int numberOfMissingTypes = u2At(missingTypeOffset);
if (numberOfMissingTypes != 0) {
this.missingTypeNames = new char[numberOfMissingTypes][][];
missingTypeOffset += 2;
for (int j = 0; j < numberOfMissingTypes; j++) {
utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(missingTypeOffset)] + 1)];
char[] missingTypeConstantPoolName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
this.missingTypeNames[j] = CharOperation.splitOn('/', missingTypeConstantPoolName);
missingTypeOffset += 2;
}
}
}
}
readOffset += (6 + u4At(readOffset + 2));
}
if (fullyInitialize) {
initialize();
}
} catch(ClassFormatException e) {
throw e;
} catch (Exception e) {
throw new ClassFormatException(
ClassFormatException.ErrTruncatedInput,
readOffset);
}
}
/** Auxiliary interface for {@link #setExternalAnnotationProvider(String,String,ZipFile,ZipFileProducer)}. */
public interface ZipFileProducer { ZipFile produce() throws IOException; }
/**
* Create and remember a provider for external annotations using the given basePath,
* which is either a directory holding .eea text files, or a zip file of entries of the same format.
* @param basePath resolved filesystem path of either directory or zip file
* @param qualifiedBinaryTypeName slash-separated type name
* @param zipFile an existing zip file for the same basePath, or null.
* Output: wl be filled with
* @param producer an optional helper to produce the zipFile when needed.
* @return the client provided zip file;
* or else a fresh new zip file, to let clients cache it, if desired;
* or null to signal that basePath is not a zip file, but a directory.
* @throws IOException any unexpected errors during file access. File not found while
* accessing an individual file if basePath is a directory is expected,
* and simply answered with null. If basePath is neither a directory nor a zip file,
* this is unexpected.
*/
public ZipFile setExternalAnnotationProvider(String basePath, String qualifiedBinaryTypeName, ZipFile zipFile, ZipFileProducer producer) throws IOException {
this.externalAnnotationStatus = ExternalAnnotationStatus.NO_EEA_FILE;
String qualifiedBinaryFileName = qualifiedBinaryTypeName + ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX;
if (zipFile == null) {
File annotationBase = new File(basePath);
if (annotationBase.isDirectory()) {
try {
String filePath = annotationBase.getAbsolutePath()+'/'+qualifiedBinaryFileName;
this.annotationProvider = new ExternalAnnotationProvider(new FileInputStream(filePath), String.valueOf(getName()));
this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED;
} catch (FileNotFoundException e) {
// expected, no need to report an error here
}
return null; // no zipFile
}
if (!annotationBase.exists())
return null; // no zipFile, treat as not-yet-created directory
zipFile = (producer != null ? producer.produce() : new ZipFile(annotationBase));
}
ZipEntry entry = zipFile.getEntry(qualifiedBinaryFileName);
if (entry != null) {
this.annotationProvider = new ExternalAnnotationProvider(zipFile.getInputStream(entry), String.valueOf(getName()));
this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED;
}
return zipFile;
}
public boolean hasAnnotationProvider() {
return this.annotationProvider != null;
}
public void markAsFromSource() {
this.externalAnnotationStatus = ExternalAnnotationStatus.FROM_SOURCE;
}
@Override
public ExternalAnnotationStatus getExternalAnnotationStatus() {
return this.externalAnnotationStatus;
}
/**
* Conditionally add external annotations to the mix.
* If 'member' is given it must be either of IBinaryField or IBinaryMethod, in which case we're seeking annotations for that member.
* Otherwise we're seeking annotations for top-level elements of a type (type parameters & super types).
*/
@Override
public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member, LookupEnvironment environment) {
if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER && this.annotationProvider != null) {
if (member == null) {
return this.annotationProvider.forTypeHeader(environment);
} else if (member instanceof IBinaryField) {
IBinaryField field = (IBinaryField) member;
char[] fieldSignature = field.getGenericSignature();
if (fieldSignature == null)
fieldSignature = field.getTypeName();
return this.annotationProvider.forField(field.getName(), fieldSignature, environment);
} else if (member instanceof IBinaryMethod) {
IBinaryMethod method = (IBinaryMethod) member;
char[] methodSignature = method.getGenericSignature();
if (methodSignature == null)
methodSignature = method.getMethodDescriptor();
return this.annotationProvider.forMethod(method.isConstructor() ? TypeConstants.INIT : method.getSelector(), methodSignature, environment);
}
}
return walker;
}
/**
* 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;
}
}
}
private void decodeTypeAnnotations(int offset, boolean runtimeVisible) {
int numberOfAnnotations = u2At(offset + 6);
if (numberOfAnnotations > 0) {
int readOffset = offset + 8;
TypeAnnotationInfo[] newInfos = null;
newInfos = new TypeAnnotationInfo[numberOfAnnotations];
for (int i = 0; i < numberOfAnnotations; i++) {
// With the last parameter being 'false', the data structure will not be flushed out
TypeAnnotationInfo newInfo = new TypeAnnotationInfo(this.reference, this.constantPoolOffsets, readOffset, runtimeVisible, false);
readOffset += newInfo.readOffset;
newInfos[i] = newInfo;
}
if (this.typeAnnotations == null) {
this.typeAnnotations = newInfos;
} else {
int length = this.typeAnnotations.length;
TypeAnnotationInfo[] temp = new TypeAnnotationInfo[length + numberOfAnnotations];
System.arraycopy(this.typeAnnotations, 0, temp, 0, length);
System.arraycopy(newInfos, 0, temp, length, numberOfAnnotations);
this.typeAnnotations = temp;
}
}
}
/**
* @return the annotations or null if there is none.
*/
public IBinaryAnnotation[] getAnnotations() {
return this.annotations;
}
/**
* @return the type annotations or null if there is none.
*/
public IBinaryTypeAnnotation[] getTypeAnnotations() {
return this.typeAnnotations;
}
/**
* 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;
}
public char[] getEnclosingMethod() {
if (this.enclosingNameAndTypeIndex <= 0) {
return null;
}
if (this.enclosingMethod == null) {
// read the name
StringBuffer buffer = new StringBuffer();
int nameAndTypeOffset = this.constantPoolOffsets[this.enclosingNameAndTypeIndex];
int utf8Offset = this.constantPoolOffsets[u2At(nameAndTypeOffset + 1)];
buffer.append(utf8At(utf8Offset + 3, u2At(utf8Offset + 1)));
utf8Offset = this.constantPoolOffsets[u2At(nameAndTypeOffset + 3)];
buffer.append(utf8At(utf8Offset + 3, u2At(utf8Offset + 1)));
this.enclosingMethod = String.valueOf(buffer).toCharArray();
}
return this.enclosingMethod;
}
/*
* 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;
}
/*
public static void main(String[] args) throws ClassFormatException, IOException {
if (args == null || args.length != 1) {
System.err.println("ClassFileReader "); //$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('}');
}
*/
public char[][][] getMissingTypeNames() {
return this.missingTypeNames;
}
/**
* 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() {
int modifiers;
if (this.innerInfo != null) {
modifiers = this.innerInfo.getModifiers()
| (this.accessFlags & ClassFileConstants.AccDeprecated)
| (this.accessFlags & ClassFileConstants.AccSynthetic);
} else {
modifiers = this.accessFlags;
}
return modifiers;
}
/**
* 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 (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.HierarchyHasProblems; // different hierarchy status ?
// meta-annotations
if ((getTagBits() & OnlyStructuralTagBits) != (newClassFile.getTagBits() & OnlyStructuralTagBits))
return true;
// annotations
if (hasStructuralAnnotationChanges(getAnnotations(), newClassFile.getAnnotations()))
return true;
if (this.version >= ClassFileConstants.JDK1_8
&& hasStructuralTypeAnnotationChanges(getTypeAnnotations(), newClassFile.getTypeAnnotations()))
return true;
// generic signature
if (!CharOperation.equals(getGenericSignature(), newClassFile.getGenericSignature()))
return true;
// superclass
if (!CharOperation.equals(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 = 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;
}
}
// missing types
char[][][] missingTypes = getMissingTypeNames();
char[][][] newMissingTypes = newClassFile.getMissingTypeNames();
if (missingTypes != null) {
if (newMissingTypes == null) {
return true;
}
int length = missingTypes.length;
if (length != newMissingTypes.length) {
return true;
}
for (int i = 0; i < length; i++) {
if (!CharOperation.equals(missingTypes[i], newMissingTypes[i])) {
return true;
}
}
} else if (newMissingTypes != null) {
return true;
}
return false;
} catch (ClassFormatException e) {
return true;
}
}
private boolean hasStructuralAnnotationChanges(IBinaryAnnotation[] currentAnnotations, IBinaryAnnotation[] otherAnnotations) {
if (currentAnnotations == otherAnnotations)
return false;
int currentAnnotationsLength = currentAnnotations == null ? 0 : currentAnnotations.length;
int otherAnnotationsLength = otherAnnotations == null ? 0 : otherAnnotations.length;
if (currentAnnotationsLength != otherAnnotationsLength)
return true;
for (int i = 0; i < currentAnnotationsLength; i++) {
Boolean match = matchAnnotations(currentAnnotations[i], otherAnnotations[i]);
if (match != null)
return match.booleanValue();
}
return false;
}
private Boolean matchAnnotations(IBinaryAnnotation currentAnnotation, IBinaryAnnotation otherAnnotation) {
if (!CharOperation.equals(currentAnnotation.getTypeName(), otherAnnotation.getTypeName()))
return true;
IBinaryElementValuePair[] currentPairs = currentAnnotation.getElementValuePairs();
IBinaryElementValuePair[] otherPairs = otherAnnotation.getElementValuePairs();
int currentPairsLength = currentPairs == null ? 0 : currentPairs.length;
int otherPairsLength = otherPairs == null ? 0 : otherPairs.length;
if (currentPairsLength != otherPairsLength)
return Boolean.TRUE;
for (int j = 0; j < currentPairsLength; j++) {
if (!CharOperation.equals(currentPairs[j].getName(), otherPairs[j].getName()))
return Boolean.TRUE;
final Object value = currentPairs[j].getValue();
final Object value2 = otherPairs[j].getValue();
if (value instanceof Object[]) {
Object[] currentValues = (Object[]) value;
if (value2 instanceof Object[]) {
Object[] currentValues2 = (Object[]) value2;
final int length = currentValues.length;
if (length != currentValues2.length) {
return Boolean.TRUE;
}
for (int n = 0; n < length; n++) {
if (!currentValues[n].equals(currentValues2[n])) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
return Boolean.TRUE;
} else if (!value.equals(value2)) {
return Boolean.TRUE;
}
}
return null;
}
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 (hasStructuralAnnotationChanges(currentFieldInfo.getAnnotations(), otherFieldInfo.getAnnotations()))
return true;
if (this.version >= ClassFileConstants.JDK1_8
&& hasStructuralTypeAnnotationChanges(currentFieldInfo.getTypeAnnotations(), otherFieldInfo.getTypeAnnotations()))
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 (hasStructuralAnnotationChanges(currentMethodInfo.getAnnotations(), otherMethodInfo.getAnnotations()))
return true;
// parameter annotations:
int currentAnnotatedParamsCount = currentMethodInfo.getAnnotatedParametersCount();
int otherAnnotatedParamsCount = otherMethodInfo.getAnnotatedParametersCount();
if (currentAnnotatedParamsCount != otherAnnotatedParamsCount)
return true;
for (int i=0; i= ClassFileConstants.JDK1_8
&& hasStructuralTypeAnnotationChanges(currentMethodInfo.getTypeAnnotations(), otherMethodInfo.getTypeAnnotations()))
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;
}
private boolean hasStructuralTypeAnnotationChanges(IBinaryTypeAnnotation[] currentTypeAnnotations, IBinaryTypeAnnotation[] otherTypeAnnotations) {
if (otherTypeAnnotations != null) {
// copy so we can delete matched annotations:
int len = otherTypeAnnotations.length;
System.arraycopy(otherTypeAnnotations, 0, otherTypeAnnotations = new IBinaryTypeAnnotation[len], 0, len);
}
if (currentTypeAnnotations != null) {
loopCurrent:
for (IBinaryTypeAnnotation currentAnnotation : currentTypeAnnotations) {
if (!affectsSignature(currentAnnotation)) continue;
if (otherTypeAnnotations == null)
return true;
for (int i = 0; i < otherTypeAnnotations.length; i++) {
IBinaryTypeAnnotation otherAnnotation = otherTypeAnnotations[i];
if (otherAnnotation != null && matchAnnotations(currentAnnotation.getAnnotation(), otherAnnotation.getAnnotation()) == Boolean.TRUE) {
otherTypeAnnotations[i] = null; // matched
continue loopCurrent;
}
}
return true; // not matched
}
}
if (otherTypeAnnotations != null) {
for (IBinaryTypeAnnotation otherAnnotation : otherTypeAnnotations) {
if (affectsSignature(otherAnnotation))
return true;
}
}
return false;
}
private boolean affectsSignature(IBinaryTypeAnnotation typeAnnotation) {
if (typeAnnotation == null) return false;
int targetType = typeAnnotation.getTargetType();
if (targetType >= AnnotationTargetTypeConstants.LOCAL_VARIABLE && targetType <= AnnotationTargetTypeConstants.METHOD_REFERENCE_TYPE_ARGUMENT)
return false; // affects detail within a block
return true;
}
/**
* 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 = this.fieldsCount; i < max; i++) {
this.fields[i].initialize();
}
for (int i = 0, max = this.methodsCount; i < max; i++) {
this.methods[i].initialize();
}
if (this.innerInfos != null) {
for (int i = 0, max = this.innerInfos.length; i < max; i++) {
this.innerInfos[i].initialize();
}
}
if (this.annotations != null) {
for (int i = 0, max = this.annotations.length; i < max; i++) {
this.annotations[i].initialize();
}
}
this.getEnclosingMethod();
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 boolean
*/
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 boolean
*/
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 boolean
*/
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 boolean
*/
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(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(accessFlags()) + "(" + accessFlags() + ")"); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
print.flush();
return out.toString();
}
}