private void markAnnotationDependenciesRecursively(final Dependency[] dependencies, final @NonNls String reason, final TIntHashSet visitedAnnotations) throws CacheCorruptedException { final Cache oldCache = myDependencyCache.getCache(); for (Dependency dependency : dependencies) { if (myDependencyCache.markTargetClassInfo(dependency)) { if (LOG.isDebugEnabled()) { LOG.debug("Mark dependent class " + myDependencyCache.resolve(dependency.getClassQualifiedName()) + reason); } } final int depQName = dependency.getClassQualifiedName(); if (ClsUtil.isAnnotation(oldCache.getFlags(depQName))) { if (!visitedAnnotations.contains(depQName)) { visitedAnnotations.add(depQName); markAnnotationDependenciesRecursively(oldCache.getBackDependencies(depQName), LOG.isDebugEnabled()? "; reason: cascade semantics change for " + myDependencyCache.resolve(depQName) : "", visitedAnnotations); } } } }
private boolean hasBaseAbstractMethods(int qName, Set methodsToCheck) throws CacheCorruptedException { final SymbolTable symbolTable = myDependencyCache.getSymbolTable(); final Cache oldCache = myDependencyCache.getCache(); final Cache newCache = myDependencyCache.getNewClassesCache(); final Cache cache = newCache.containsClass(qName)? newCache : oldCache; // use recompiled version (if any) for searching methods for (Iterator it = methodsToCheck.iterator(); it.hasNext();) { final MethodInfo methodInfo = (MethodInfo)it.next(); final MethodInfo superMethod = cache.findMethodsBySignature(qName, methodInfo.getDescriptor(symbolTable), symbolTable); if (superMethod != null) { if (ClsUtil.isAbstract(superMethod.getFlags())) { return true; } it.remove(); } } return false; }
private void findModifiedConstants( final int qName, Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> changedConstants, Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> removedConstants) throws CacheCorruptedException { final Cache cache = getCache(); for (final FieldInfo field : cache.getFields(qName)) { final int oldFlags = field.getFlags(); if (ClsUtil.isStatic(oldFlags) && ClsUtil.isFinal(oldFlags)) { final Cache newClassesCache = getNewClassesCache(); FieldInfo newField = newClassesCache.findFieldByName(qName, field.getName()); if (newField == null) { if (!ConstantValue.EMPTY_CONSTANT_VALUE.equals(field.getConstantValue())) { // if the field was really compile time constant removedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field)); } } else { final boolean visibilityRestricted = MakeUtil.isMoreAccessible(oldFlags, newField.getFlags()); if (!field.getConstantValue().equals(newField.getConstantValue()) || visibilityRestricted) { changedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field, visibilityRestricted)); } } } } }
private void initConstantPool() throws ClsFormatException { if (myConstantPoolOffsets == null){ BytePointer ptr = new BytePointer(getData(), 0); ConstantPoolIterator iterator = new ConstantPoolIterator(ptr); myConstantPoolOffsets = new int[iterator.getEntryCount()]; myConstantPoolOffsets[0] = iterator.getCurrentOffset(); int index = 1; while (iterator.hasMoreEntries()) { int tag = ClsUtil.readU1(ptr); if (tag == ClsUtil.CONSTANT_Long || tag == ClsUtil.CONSTANT_Double) { myConstantPoolOffsets[index++] = ptr.offset + 8; // takes 2 entries! } iterator.next(); myConstantPoolOffsets[index++] = iterator.getCurrentOffset(); } } }
private ConstantValue readConstant(final BytePointer ptr) throws ClsFormatException { final int tag = ClsUtil.readU1(ptr); switch (tag) { case ClsUtil.CONSTANT_Integer : int value = ClsUtil.readU4(ptr); return new IntegerConstantValue(value); case ClsUtil.CONSTANT_Float: float floatValue = ClsUtil.readFloat(ptr); return new FloatConstantValue(floatValue); case ClsUtil.CONSTANT_Long : int high = ClsUtil.readU4(ptr); int low = ClsUtil.readU4(ptr); long v = ((long)high << 32) | (low & 0xFFFFFFFFL); return new LongConstantValue(v); case ClsUtil.CONSTANT_Double : double doubleValue = ClsUtil.readDouble(ptr); return new DoubleConstantValue(doubleValue); case ClsUtil.CONSTANT_String : int stringIndex = ClsUtil.readU2(ptr); ptr.offset = getOffsetInConstantPool(stringIndex); return new StringConstantValue(ClsUtil.readUtf8Info(ptr)); default : throw new ClsFormatException(); } }
public ConstantPoolIterator(BytePointer ptr) throws ClsFormatException { myPtr = ptr; myPtr.offset = 0; int magic = ClsUtil.readU4(myPtr); if (magic != ClsUtil.MAGIC){ throw new ClsFormatException(); } myPtr.offset += 2; // minor version myPtr.offset += 2; // major version myEntryCount = ClsUtil.readU2(myPtr); if (myEntryCount < 1){ throw new ClsFormatException(); } myCurrentEntryIndex = 1; // Entry at index 0 is included in the count but is not present in the constant pool myCurrentOffset = myPtr.offset; }
private void markAnnotationDependenciesRecursively(final Dependency[] dependencies, final @NonNls String reason, final TIntHashSet visitedAnnotations) throws CacheCorruptedException { final Cache oldCache = myJavaDependencyCache.getCache(); for (Dependency dependency : dependencies) { if (myJavaDependencyCache.markTargetClassInfo(dependency)) { if (LOG.isDebugEnabled()) { LOG.debug("Mark dependent class " + myJavaDependencyCache.resolve(dependency.getClassQualifiedName()) + reason); } } final int depQName = dependency.getClassQualifiedName(); if (ClsUtil.isAnnotation(oldCache.getFlags(depQName))) { if (!visitedAnnotations.contains(depQName)) { visitedAnnotations.add(depQName); markAnnotationDependenciesRecursively(oldCache.getBackDependencies(depQName), LOG.isDebugEnabled()? "; reason: cascade semantics change for " + myJavaDependencyCache.resolve(depQName) : "", visitedAnnotations); } } } }
private boolean hasBaseAbstractMethods(int qName, Set methodsToCheck) throws CacheCorruptedException { final SymbolTable symbolTable = myJavaDependencyCache.getSymbolTable(); final Cache oldCache = myJavaDependencyCache.getCache(); final Cache newCache = myJavaDependencyCache.getNewClassesCache(); final Cache cache = newCache.containsClass(qName)? newCache : oldCache; // use recompiled version (if any) for searching methods for (Iterator it = methodsToCheck.iterator(); it.hasNext();) { final MethodInfo methodInfo = (MethodInfo)it.next(); final MethodInfo superMethod = cache.findMethodsBySignature(qName, methodInfo.getDescriptor(symbolTable), symbolTable); if (superMethod != null) { if (ClsUtil.isAbstract(superMethod.getFlags())) { return true; } it.remove(); } } return false; }
private void findModifiedConstants(final int qName, Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> changedConstants, Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> removedConstants) throws CacheCorruptedException { final Cache cache = getCache(); for (final FieldInfo field : cache.getFields(qName)) { final int oldFlags = field.getFlags(); if (ClsUtil.isStatic(oldFlags) && ClsUtil.isFinal(oldFlags)) { final Cache newClassesCache = getNewClassesCache(); FieldInfo newField = newClassesCache.findFieldByName(qName, field.getName()); if (newField == null) { if (!ConstantValue.EMPTY_CONSTANT_VALUE.equals(field.getConstantValue())) { // if the field was really compile time constant removedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field)); } } else { final boolean visibilityRestricted = JavaMakeUtil.isMoreAccessible(oldFlags, newField.getFlags()); if (!field.getConstantValue().equals(newField.getConstantValue()) || visibilityRestricted) { changedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field, visibilityRestricted)); } } } } }
/** * tests if the accessibility, denoted by flags1 is less restricted than the accessibility denoted by flags2 * * @return true means flags1 is less restricted than flags2 <br> * false means flags1 define more restricted access than flags2 or they have equal accessibility */ public static boolean isMoreAccessible(int flags1, int flags2) { if(ClsUtil.isPrivate(flags2)) { return ClsUtil.isPackageLocal(flags1) || ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1); } if(ClsUtil.isPackageLocal(flags2)) { return ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1); } if(ClsUtil.isProtected(flags2)) { return ClsUtil.isPublic(flags1); } return false; }
private static boolean hasGenericsNameClashes(final MethodInfo baseMethod, final Cache oldCache, final int subclassQName) throws CacheCorruptedException { // it is illegal if 2 methods in a hierarchy have 1) same name 2) different signatures 3) same erasure final List<MethodInfo> methods = oldCache.findMethodsByName(subclassQName, baseMethod.getName()); if (methods.size() > 0) { for (final MethodInfo methodInSubclass : methods) { if (ClsUtil.isBridge(methodInSubclass.getFlags())) { continue; } if (baseMethod.getDescriptor() == methodInSubclass.getDescriptor() && baseMethod.getGenericSignature() != methodInSubclass.getGenericSignature()) { return true; } } } return false; }
/** * tests if the accessibility, denoted by flags1 is less restricted than the accessibility denoted by flags2 * @return true means flags1 is less restricted than flags2 <br> * false means flags1 define more restricted access than flags2 or they have equal accessibility */ public static boolean isMoreAccessible(int flags1, int flags2) { if (ClsUtil.isPrivate(flags2)) { return ClsUtil.isPackageLocal(flags1) || ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1); } if (ClsUtil.isPackageLocal(flags2)) { return ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1); } if (ClsUtil.isProtected(flags2)) { return ClsUtil.isPublic(flags1); } return false; }
public String getQualifiedName() throws ClsFormatException { if (myQualifiedName == null) { BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 2); ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); int tag = ClsUtil.readU1(ptr); if (tag != ClsUtil.CONSTANT_Class){ throw new ClsFormatException(); } ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); myQualifiedName = ClsUtil.readUtf8Info(ptr, '/', '.'); // keep '$' in the names } return myQualifiedName; }
/** * @return fully qualified name of the class' superclass. In case there is no super return "" */ public String getSuperClass() throws ClsFormatException { if (mySuperClassName == null) { BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 4); int index = ClsUtil.readU2(ptr); if (index == 0) { if (CommonClassNames.JAVA_LANG_OBJECT.equals(getQualifiedName())) { mySuperClassName = ""; } else { throw new ClsFormatException(); } } else { ptr.offset = getOffsetInConstantPool(index); mySuperClassName = readClassInfo(ptr); // keep '$' in the name for anonymous classes if (isInterface()) { if (!CommonClassNames.JAVA_LANG_OBJECT.equals(mySuperClassName)) { throw new ClsFormatException(); } } /* else { if (!MakeUtil.isAnonymous(mySuperClassName)) { mySuperClassName = mySuperClassName.replace('$', '.'); } } */ } } return mySuperClassName; }
public String[] getSuperInterfaces() throws ClsFormatException { if (mySuperInterfaces == null) { BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 6); int count = ClsUtil.readU2(ptr); mySuperInterfaces = ArrayUtil.newStringArray(count); BytePointer auxPtr = new BytePointer(ptr.bytes, 0); for (int idx = 0; idx < mySuperInterfaces.length; idx++) { auxPtr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); mySuperInterfaces[idx] = readClassInfo(auxPtr); } } return mySuperInterfaces; }
private void parseConstantPool() throws ClsFormatException { if (myReferences != null) { return; } myReferences = new ArrayList<ReferenceInfo>(); initConstantPool(); final BytePointer ptr = new BytePointer(getData(), 0); ConstantPoolIterator iterator = new ConstantPoolIterator(ptr); while (iterator.hasMoreEntries()) { final int tag = ClsUtil.readU1(ptr); if (tag == ClsUtil.CONSTANT_Fieldref || tag == ClsUtil.CONSTANT_Methodref || tag == ClsUtil.CONSTANT_InterfaceMethodref) { //ptr.offset -= 1; // position to the beginning of the structure MemberReferenceInfo refInfo = readRefStructure(tag, ptr); if (refInfo != null) { myReferences.add(refInfo); } /* String name = mySymbolTable.getSymbol(refInfo.getMemberInfo().getName()); if (name.indexOf('$') < 0 && name.indexOf('<') < 0) { // skip refs to synthetic members myReferences.add(refInfo); } else if ("<init>".equals(name)) { // add instance initializers (constructors) myReferences.add(refInfo); } else { System.out.println("ReferenceInfo thrown out: " + mySymbolTable.getSymbol(refInfo.getClassName()) + "." + mySymbolTable.getSymbol(refInfo.getMemberInfo().getName())); ourWasteReferenceObjectsCounter += 1; } */ } else if (tag == ClsUtil.CONSTANT_Class) { ptr.offset -= 1; // position to the beginning of the structure String className = readClassInfo(ptr); myReferences.add(new ReferenceInfo(getSymbolId(className))); } iterator.next(); } //System.out.println("ourWasteReferenceObjectsCounter = " + ourWasteReferenceObjectsCounter); }
private MemberReferenceInfo readRefStructure(int tag, BytePointer ptr) throws ClsFormatException { /* if (tag != ClsUtil.CONSTANT_Fieldref && tag != ClsUtil.CONSTANT_Methodref && tag != ClsUtil.CONSTANT_InterfaceMethodref) { throw new ClsFormatException(); } */ int classInfoIndex = ClsUtil.readU2(ptr); int nameTypeInfoIndex = ClsUtil.readU2(ptr); ptr.offset = getOffsetInConstantPool(classInfoIndex); if (ClsUtil.CONSTANT_Class != ClsUtil.readU1(ptr)) { throw new ClsFormatException(); } ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); String className = ClsUtil.readUtf8Info(ptr, '/', '.'); // keep '$' in names ptr.offset = getOffsetInConstantPool(nameTypeInfoIndex); if (ClsUtil.CONSTANT_NameAndType != ClsUtil.readU1(ptr)) { throw new ClsFormatException(); } int memberNameIndex = ClsUtil.readU2(ptr); int descriptorIndex = ClsUtil.readU2(ptr); ptr.offset = getOffsetInConstantPool(memberNameIndex); String memberName = ClsUtil.readUtf8Info(ptr); if ((memberName.indexOf('$') >= 0 || memberName.indexOf('<') >= 0) && !CONSTRUCTOR_NAME.equals(memberName)) { // skip refs to synthetic members return null; } ptr.offset = getOffsetInConstantPool(descriptorIndex); String descriptor = ClsUtil.readUtf8Info(ptr); MemberInfo info = ClsUtil.CONSTANT_Fieldref == tag? new FieldInfo(getSymbolId(memberName), getSymbolId(descriptor)) : new MethodInfo(getSymbolId(memberName), getSymbolId(descriptor), CONSTRUCTOR_NAME.equals(memberName)); return new MemberReferenceInfo(getSymbolId(className), info); }
private String readClassInfo(BytePointer ptr) throws ClsFormatException{ final int tag = ClsUtil.readU1(ptr); if (tag != ClsUtil.CONSTANT_Class){ throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.record.tag.expected.another", tag, ClsUtil.CONSTANT_Class)); } int index = ClsUtil.readU2(ptr); return ClsUtil.readUtf8Info(new BytePointer(ptr.bytes, getOffsetInConstantPool(index)), '/', '.'); }
@SuppressWarnings({"HardCodedStringLiteral"}) private ClsAttributeTable readAttributes(BytePointer ptr) throws ClsFormatException { int count = ClsUtil.readU2(ptr); // attributeCount final ClsAttributeTable attributes = new ClsAttributeTable(); while (count-- > 0) { final String attrName = readAttributeName(ptr); if ("Exceptions".equals(attrName)) { attributes.exceptions = readExceptions(ptr); } else if ("Signature".equals(attrName)) { attributes.genericSignature = readSignatureAttribute(ptr); } else if ("SourceFile".equals(attrName)) { attributes.sourceFile = readSourceFileAttribute(ptr); } else if ("ConstantValue".equals(attrName)){ attributes.constantValue = readFieldConstantValue(ptr); } else if ("RuntimeVisibleAnnotations".equals(attrName)) { attributes.runtimeVisibleAnnotations = readAnnotations(ptr); } else if ("RuntimeInvisibleAnnotations".equals(attrName)) { attributes.runtimeInvisibleAnnotations = readAnnotations(ptr); } else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) { attributes.runtimeVisibleParameterAnnotations = readParameterAnnotations(ptr); } else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) { attributes.runtimeInvisibleParameterAnnotations = readParameterAnnotations(ptr); } else if ("AnnotationDefault".equals(attrName)) { attributes.annotationDefault = readAnnotationMemberValue(new BytePointer(ptr.bytes, ptr.offset + 6)); } gotoNextAttribute(ptr); } return attributes; }
/** Signature_attribute { u2 attribute_name_index; (must be equal to "Signature") u4 attribute_length; (must be equal to 2) u2 signature_index; } */ private String readSignatureAttribute(BytePointer p) throws ClsFormatException { final BytePointer ptr = new BytePointer(p.bytes, p.offset + 2); // position to the length if (ClsUtil.readU4(ptr) != 2) { return null; } ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); return ClsUtil.readUtf8Info(ptr); }
private String[] readExceptions(BytePointer p) throws ClsFormatException{ final BytePointer ptr = new BytePointer(p.bytes, p.offset + 6); // position to the count of exceptions int count = ClsUtil.readU2(ptr); final ArrayList<String> array = new ArrayList<String>(count); while (count-- > 0) { int idx = ClsUtil.readU2(ptr); if (idx != 0) { final String exceptionClass = readClassInfo(new BytePointer(ptr.bytes, getOffsetInConstantPool(idx))); array.add(exceptionClass); } } return ArrayUtil.toStringArray(array); }
private String readSourceFileAttribute(BytePointer p) throws ClsFormatException { BytePointer ptr = new BytePointer(p.bytes, p.offset + 2); // position to the length if (ClsUtil.readU4(ptr) != 2) { return null; } ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr)); String path = ClsUtil.readUtf8Info(ptr); // jdk version 1.3.0 puts full path to the source, but later versions store only short name final int slashIndex = path.lastIndexOf('/'); if (slashIndex >= 0) { path = path.substring(slashIndex + 1, path.length()); } return path; }
private ConstantValue readFieldConstantValue(BytePointer p) throws ClsFormatException{ final BytePointer ptr = new BytePointer(p.bytes, p.offset + 2); if (ClsUtil.readU4(ptr) != 2) { throw new ClsFormatException(); // attribute length must be 2 } int valueIndex = ClsUtil.readU2(ptr); ptr.offset = getOffsetInConstantPool(valueIndex); return readConstant(ptr); }
private AnnotationConstantValue[][] readParameterAnnotations(BytePointer p) throws ClsFormatException { final BytePointer ptr = new BytePointer(p.bytes, p.offset + 6); // position to the number of parameters final int numberOfParams = ClsUtil.readU1(ptr); if (numberOfParams == 0) { return null; } final AnnotationConstantValue[][] annotations = new AnnotationConstantValue[numberOfParams][]; for (int parameterIndex = 0; parameterIndex < numberOfParams; parameterIndex++) { annotations[parameterIndex] = readAnnotationsArray(ptr); } return annotations; }
private AnnotationConstantValue[] readAnnotationsArray(BytePointer ptr) throws ClsFormatException { final int numberOfAnnotations = ClsUtil.readU2(ptr); if (numberOfAnnotations == 0) { return AnnotationConstantValue.EMPTY_ARRAY; } AnnotationConstantValue[] annotations = new AnnotationConstantValue[numberOfAnnotations]; for (int attributeIndex = 0; attributeIndex < numberOfAnnotations; attributeIndex++) { annotations[attributeIndex] = readAnnotation(ptr); } return annotations; }
private AnnotationConstantValue readAnnotation(BytePointer ptr) throws ClsFormatException { final int classInfoIndex = ClsUtil.readU2(ptr); final String qName = readAnnotationClassName(new BytePointer(ptr.bytes, getOffsetInConstantPool(classInfoIndex))); final List<AnnotationNameValuePair> memberValues = new ArrayList<AnnotationNameValuePair>(); final int numberOfPairs = ClsUtil.readU2(ptr); for (int idx = 0; idx < numberOfPairs; idx++) { final int memberNameIndex = ClsUtil.readU2(ptr); final String memberName = ClsUtil.readUtf8Info(ptr.bytes, getOffsetInConstantPool(memberNameIndex)); final ConstantValue memberValue = readAnnotationMemberValue(ptr); memberValues.add(new AnnotationNameValuePair(getSymbolId(memberName), memberValue)); } return new AnnotationConstantValue(getSymbolId(qName), memberValues.toArray(new AnnotationNameValuePair[memberValues.size()])); }
private String readAnnotationClassName(BytePointer ptr) throws ClsFormatException { // TODO: need this method because because of incomplete class format spec at the moment of writing // it is not clear what structure is expected: CONSTANT_Utf8 or CONSTANT_Class final int tag = ClsUtil.readU1(ptr); if (tag == ClsUtil.CONSTANT_Utf8) { return ClsUtil.getTypeText(ptr.bytes, ptr.offset + 2); //skip length } if (tag == ClsUtil.CONSTANT_Class){ ptr.offset -= 1; // rollback return readClassInfo(ptr); } //noinspection HardCodedStringLiteral throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.record.tag.expected.another", tag, "CONSTANT_Utf8(" + ClsUtil.CONSTANT_Utf8 + ") / CONSTANT_Class(" + ClsUtil.CONSTANT_Class + ")")); }