package org.eclipse.jdt.internal.compiler.classfmt;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.function.Predicate;
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.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
import org.eclipse.jdt.internal.compiler.env.IBinaryField;
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
import org.eclipse.jdt.internal.compiler.env.IBinaryModule;
import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
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 ModuleInfo moduleDeclaration;
public char[] moduleName;
private int fieldsCount;
private InnerClassInfo innerInfo;
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 char[] nestHost;
private int nestMembersCount;
private char[][] nestMembers;
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 ");
if ((modifiers & ClassFileConstants.AccPrivate) != 0) print.print("private ");
if ((modifiers & ClassFileConstants.AccFinal) != 0) print.print("final ");
if ((modifiers & ClassFileConstants.AccSuper) != 0) print.print("super ");
if ((modifiers & ClassFileConstants.AccInterface) != 0) print.print("interface ");
if ((modifiers & ClassFileConstants.AccAbstract) != 0) print.print("abstract ");
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 readFromJrt(
File jrt,
IModule module,
String filename)
throws ClassFormatException, java.io.IOException {
return JRTUtil.getClassfile(jrt, filename, module);
}
public static ClassFileReader readFromModule(
File jrt,
String moduleName,
String filename,
Predicate<String> moduleNameFilter)
throws ClassFormatException, java.io.IOException {
return JRTUtil.getClassfile(jrt, filename, moduleName, moduleNameFilter);
}
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);
}
public ClassFileReader(byte classFileBytes[], char[] fileName) throws ClassFormatException {
this(classFileBytes, fileName, false);
}
public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInitialize) throws ClassFormatException {
super(classFileBytes, null, 0);
this.classFileName = fileName;
int readOffset = 10;
try {
this.version = ((long)u2At(6) << 16) + u2At(4);
this.constantPoolCount = u2At(8);
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.DynamicTag :
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantDynamicFixedSize;
break;
case ClassFileConstants.InvokeDynamicTag :
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantInvokeDynamicFixedSize;
break;
case ClassFileConstants.ModuleTag:
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantModuleFixedSize;
break;
case ClassFileConstants.PackageTag:
this.constantPoolOffsets[i] = readOffset;
readOffset += ClassFileConstants.ConstantPackageFixedSize;
break;
}
}
this.accessFlags = u2At(readOffset);
readOffset += 2;
this.classNameIndex = u2At(readOffset);
if (this.classNameIndex != 0) {
this.className = getConstantClassNameAt(this.classNameIndex);
}
readOffset += 2;
int superclassNameIndex = u2At(readOffset);
readOffset += 2;
if (superclassNameIndex != 0) {
this.superclassName = getConstantClassNameAt(superclassNameIndex);
}
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;
}
}
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.version);
this.fields[i] = field;
readOffset += field.sizeInBytes();
}
}
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, this.version)
: MethodInfo.createMethod(this.reference, this.constantPoolOffsets, readOffset, this.version);
readOffset += this.methods[i].sizeInBytes();
}
}
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];
}
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)) {
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;
}
}
} else if (CharOperation.equals(attributeName, AttributeNamesConstants.ModuleName)) {
this.moduleDeclaration = ModuleInfo.createModule(this.reference, this.constantPoolOffsets, readOffset);
this.moduleName = this.moduleDeclaration.name();
}
break;
case 'N' :
if (CharOperation.equals(attributeName, AttributeNamesConstants.NestHost)) {
utf8Offset =
this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(readOffset + 6)] + 1)];
this.nestHost = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
} else if (CharOperation.equals(attributeName, AttributeNamesConstants.NestMembers)) {
int offset = readOffset + 6;
this.nestMembersCount = u2At(offset);
if (this.nestMembersCount != 0) {
offset += 2;
this.nestMembers = new char[this.nestMembersCount][];
for (int j = 0; j < this.nestMembersCount; j++) {
utf8Offset =
this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(offset)] + 1)];
this.nestMembers[j] = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
offset += 2;
}
}
}
break;
}
readOffset += (6 + u4At(readOffset + 2));
}
if (this.moduleDeclaration != null && this.annotations != null) {
this.moduleDeclaration.setAnnotations(this.annotations, this.tagBits, fullyInitialize);
this.annotations = null;
}
if (fullyInitialize) {
initialize();
}
} catch(ClassFormatException e) {
throw e;
} catch (Exception e) {
throw new ClassFormatException(e,
this.classFileName,
ClassFormatException.ErrTruncatedInput,
readOffset);
}
}
public char[] getNestHost() {
return this.nestHost;
}
@Override
public ExternalAnnotationStatus getExternalAnnotationStatus() {
return ExternalAnnotationStatus.NOT_EEA_CONFIGURED;
}
@Override
public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member, LookupEnvironment environment) {
return walker;
}
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++) {
AnnotationInfo newInfo = new AnnotationInfo(this.reference, this.constantPoolOffsets, readOffset, runtimeVisible, false);
readOffset += newInfo.readOffset;
long standardTagBits = newInfo.standardAnnotationTagBits;
if (standardTagBits != 0) {
this.tagBits |= standardTagBits;
if (this.version < ClassFileConstants.JDK9 || (standardTagBits & TagBits.AnnotationDeprecated) == 0)
continue;
}
if (newInfos == null)
newInfos = new AnnotationInfo[numberOfAnnotations - i];
newInfos[newInfoCount++] = newInfo;
}
if (newInfos == null)
return;
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++) {
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;
}
}
}
@Override
public IBinaryAnnotation[] getAnnotations() {
return this.annotations;
}
@Override
public IBinaryTypeAnnotation[] getTypeAnnotations() {
return this.typeAnnotations;
}
private char[] getConstantClassNameAt(int constantPoolIndex) {
int utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[constantPoolIndex] + 1)];
return utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
}
public int[] getConstantPoolOffsets() {
return this.constantPoolOffsets;
}
@Override
public char[] getEnclosingMethod() {
if (this.enclosingNameAndTypeIndex <= 0) {
return null;
}
if (this.enclosingMethod == null) {
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;
}
@Override
public char[] getEnclosingTypeName() {
return this.enclosingTypeName;
}
@Override
public IBinaryField[] getFields() {
return this.fields;
}
@Override
public char[] getModule() {
return this.moduleName;
}
public IBinaryModule getModuleDeclaration() {
return this.moduleDeclaration;
}
@Override
public char[] getFileName() {
return this.classFileName;
}
@Override
public char[] getGenericSignature() {
return this.signature;
}
public char[] getInnerSourceName() {
if (this.innerInfo != null)
return this.innerInfo.getSourceName();
return null;
}
@Override
public char[][] getInterfaceNames() {
return this.interfaceNames;
}
@Override
public IBinaryNestedType[] getMemberTypes() {
if (this.innerInfos == null) return null;
int length = this.innerInfos.length - (this.innerInfo != null ? 1 : 0);
if (length != 0) {
IBinaryNestedType[] memberTypes =
new IBinaryNestedType[length];
int memberTypeIndex = 0;
for (InnerClassInfo currentInnerInfo : this.innerInfos) {
int outerClassNameIdx = currentInnerInfo.outerClassNameIndex;
int innerNameIndex = currentInnerInfo.innerNameIndex;
if (outerClassNameIdx != 0
&& innerNameIndex != 0
&& outerClassNameIdx == this.classNameIndex
&& currentInnerInfo.getSourceName().length != 0) {
memberTypes[memberTypeIndex++] = currentInnerInfo;
}
}
if (memberTypeIndex == 0) return null;
if (memberTypeIndex != memberTypes.length) {
System.arraycopy(
memberTypes,
0,
(memberTypes = new IBinaryNestedType[memberTypeIndex]),
0,
memberTypeIndex);
}
return memberTypes;
}
return null;
}
@Override
public IBinaryMethod[] getMethods() {
return this.methods;
}
@Override
public char[][][] getMissingTypeNames() {
return this.missingTypeNames;
}
@Override
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;
}
@Override
public char[] getName() {
return this.className;
}
@Override
public char[] getSourceName() {
if (this.sourceName != null)
return this.sourceName;
char[] name = getInnerSourceName();
if (name == null) {
name = getName();
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;
}
@Override
public char[] getSuperclassName() {
return this.superclassName;
}
@Override
public long getTagBits() {
return this.tagBits;
}
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;
}
public boolean hasStructuralChanges(byte[] newBytes) {
return hasStructuralChanges(newBytes, true, true);
}
public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, boolean excludesSynthetic) {
try {
ClassFileReader newClassFile =
new ClassFileReader(newBytes, this.classFileName);
if (getModifiers() != newClassFile.getModifiers())
return true;
long OnlyStructuralTagBits = TagBits.AnnotationTargetMASK
| TagBits.AnnotationDeprecated
| TagBits.AnnotationRetentionMASK
| TagBits.HierarchyHasProblems;
if ((getTagBits() & OnlyStructuralTagBits) != (newClassFile.getTagBits() & OnlyStructuralTagBits))
return true;
if (hasStructuralAnnotationChanges(getAnnotations(), newClassFile.getAnnotations()))
return true;
if (this.version >= ClassFileConstants.JDK1_8
&& hasStructuralTypeAnnotationChanges(getTypeAnnotations(), newClassFile.getTypeAnnotations()))
return true;
if (!CharOperation.equals(getGenericSignature(), newClassFile.getGenericSignature()))
return true;
if (!CharOperation.equals(getSuperclassName(), newClassFile.getSuperclassName()))
return true;
char[][] newInterfacesNames = newClassFile.getInterfaceNames();
if (this.interfaceNames != newInterfacesNames) {
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;
}
IBinaryNestedType[] currentMemberTypes = getMemberTypes();
IBinaryNestedType[] otherMemberTypes = newClassFile.getMemberTypes();
if (currentMemberTypes != otherMemberTypes) {
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;
}
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;
}
}
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;
}
}
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) {
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) {
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;
int currentAnnotatedParamsCount = currentMethodInfo.getAnnotatedParametersCount();
int otherAnnotatedParamsCount = otherMethodInfo.getAnnotatedParametersCount();
if (currentAnnotatedParamsCount != otherAnnotatedParamsCount)
return true;
for (int i=0; i<currentAnnotatedParamsCount; i++) {
if (hasStructuralAnnotationChanges(currentMethodInfo.getParameterAnnotations(i, this.classFileName), otherMethodInfo.getParameterAnnotations(i, this.classFileName)))
return true;
}
if (this.version >= 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) {
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) {
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;
continue loopCurrent;
}
}
return true;
}
}
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;
return true;
}
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;
}
}
@Override
public boolean isAnonymous() {
if (this.innerInfo == null) return false;
char[] innerSourceName = this.innerInfo.getSourceName();
return (innerSourceName == null || innerSourceName.length == 0);
}
@Override
public boolean isBinaryType() {
return true;
}
@Override
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);
}
@Override
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);
}
public boolean isNestedType() {
return this.innerInfo != null;
}
@Override
public char[] sourceFileName() {
return this.sourceFileName;
}
@Override
public String toString() {
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
java.io.PrintWriter print = new java.io.PrintWriter(out);
print.println(getClass().getName() + "{");
print.println(" this.className: " + new String(getName()));
print.println(" this.superclassName: " + (getSuperclassName() == null ? "null" : new String(getSuperclassName())));
if (this.moduleName != null)
print.println(" this.moduleName: " + (new String(this.moduleName)));
print.println(" access_flags: " + printTypeModifiers(accessFlags()) + "(" + accessFlags() + ")");
print.flush();
return out.toString();
}
}