@Override public void visitField(Field obj) { if (!isInnerClass && obj.isPrivate() && !obj.isSynthetic()) { String sig = obj.getSignature(); if (sig.startsWith(Values.SIG_QUALIFIED_CLASS_PREFIX)) { String type = SignatureUtils.stripSignature(sig); if (doesObjectNeedToBeWatched(type)) { fieldSpecialObjects.put(obj.getName(), obj.getSignature()); } } } }
@Override public void visit(Field obj) { int flags = obj.getAccessFlags(); if (isEjbImplClass) { ClassDescriptor fieldType = DescriptorFactory.createClassDescriptorFromFieldSignature(obj.getSignature()); if (fieldType != null) { if (Subtypes2.instanceOf(fieldType, "javax.ejb.SessionContext") || Subtypes2.instanceOf(fieldType, "javax.transaction.UserTransaction") || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBHome") || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBObject") || Subtypes2.instanceOf(fieldType, "javax.naming.Context")) { if (obj.isTransient()) bugReporter.reportBug( new BugInstance(this, "UNKNOWN", NORMAL_PRIORITY) .addClass(this) .addVisitedField(this)); return; } } } if (obj.isTransient()) { if (isSerializable && !isExternalizable) { seenTransientField = true; transientFieldsUpdates.put(getXField(), 0); } else if (reportTransientFieldOfNonSerializableClass) { bugReporter.reportBug( new BugInstance(this, "SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS", NORMAL_PRIORITY) .addClass(this) .addVisitedField(this)); } } else if (getClassName().indexOf("ObjectStreamClass") == -1 && isSerializable && !isExternalizable && getFieldSig().indexOf("L") >= 0 && !obj.isTransient() && !obj.isStatic()) { if (DEBUG) { System.out.println( "Examining non-transient field with name: " + getFieldName() + ", sig: " + getFieldSig()); } try { double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(getFieldSig()); if (DEBUG) { System.out.println(" isSerializable: " + isSerializable); } if (isSerializable < 1.0) fieldsThatMightBeAProblem.put(obj.getName(), XFactory.createXField(this)); if (isSerializable < 0.9) { // Priority is LOW for GUI classes (unless explicitly marked Serializable), // HIGH if the class directly implements Serializable, // NORMAL otherwise. int priority = computePriority(isSerializable, 0); if (!strongEvidenceForIntendedSerialization()) { if (obj.getName().startsWith("this$")) priority = Math.max(priority, NORMAL_PRIORITY); else if (innerClassHasOuterInstance) { if (isAnonymousInnerClass) priority += 2; else priority += 1; } if (isGUIClass || isEjbImplClass) priority++; } else if (isGUIClass || isEjbImplClass) priority = Math.max(priority, NORMAL_PRIORITY); if (DEBUG) System.out.println( "SE_BAD_FIELD: " + getThisClass().getClassName() + " " + obj.getName() + " " + isSerializable + " " + implementsSerializableDirectly + " " + sawSerialVersionUID + " " + isGUIClass + " " + isEjbImplClass); // Report is queued until after the entire class has been seen. if (obj.getName().equals("this$0")) fieldWarningList.add( new BugInstance(this, "SE_BAD_FIELD_INNER_CLASS", priority) .addClass(getThisClass().getClassName())); else if (isSerializable < 0.9) fieldWarningList.add( new BugInstance(this, "SE_BAD_FIELD", priority) .addClass(getThisClass().getClassName()) .addField(getDottedClassName(), obj.getName(), getFieldSig(), false)); } else if (!isGUIClass && !isEjbImplClass && obj.getName().equals("this$0")) fieldWarningList.add( new BugInstance( this, "SE_INNER_CLASS", implementsSerializableDirectly ? NORMAL_PRIORITY : LOW_PRIORITY) .addClass(getThisClass().getClassName())); } catch (ClassNotFoundException e) { if (DEBUG) { System.out.println("Caught ClassNotFoundException"); } bugReporter.reportMissingClass(e); } } if (!getFieldName().startsWith("this") && isSynthetic(obj)) foundSynthetic = true; if (!getFieldName().equals("serialVersionUID")) return; int mask = ACC_STATIC | ACC_FINAL; if (!getFieldSig().equals("I") && !getFieldSig().equals("J")) return; if ((flags & mask) == mask && getFieldSig().equals("I")) { bugReporter.reportBug( new BugInstance(this, "SE_NONLONG_SERIALVERSIONID", LOW_PRIORITY) .addClass(this) .addVisitedField(this)); sawSerialVersionUID = true; return; } else if ((flags & ACC_STATIC) == 0) { bugReporter.reportBug( new BugInstance(this, "SE_NONSTATIC_SERIALVERSIONID", NORMAL_PRIORITY) .addClass(this) .addVisitedField(this)); return; } else if ((flags & ACC_FINAL) == 0) { bugReporter.reportBug( new BugInstance(this, "SE_NONFINAL_SERIALVERSIONID", NORMAL_PRIORITY) .addClass(this) .addVisitedField(this)); return; } sawSerialVersionUID = true; }
/** * overrides the visitor to report on classes without toStrings that have fields * * @param classContext the context object of the currently parsed class */ @Override public void visitClassContext(ClassContext classContext) { JavaClass cls = classContext.getJavaClass(); if (cls.getPackageName().isEmpty()) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_PACKAGE.name(), LOW_PRIORITY) .addClass(cls)); } if ((!cls.isAbstract()) && (!cls.isEnum()) && !cls.getClassName().contains("$") && !isTestClass(cls)) { try { boolean clsHasRuntimeAnnotation = classHasRuntimeVisibleAnnotation(cls); HEStatus heStatus = HEStatus.UNKNOWN; checkIDEGeneratedParmNames(cls); for (Field f : cls.getFields()) { if (!f.isStatic() && !f.isSynthetic()) { boolean fieldHasRuntimeAnnotation = fieldHasRuntimeVisibleAnnotation(f); if (!fieldHasRuntimeAnnotation) { /* only report one of these, so as not to flood the report */ if (!hasMethodInHierarchy(cls, "toString", "()Ljava/lang/String;")) { bugReporter.reportBug( new BugInstance( this, BugType.IMC_IMMATURE_CLASS_NO_TOSTRING.name(), LOW_PRIORITY) .addClass(cls)); return; } if (heStatus != HEStatus.NOT_NEEDED) { String fieldSig = f.getSignature(); if (fieldSig.startsWith("L")) { if (!fieldSig.startsWith("Ljava")) { JavaClass fieldClass = Repository.lookupClass(fieldSig.substring(1, fieldSig.length() - 1)); if (!hasMethodInHierarchy(fieldClass, "equals", "(Ljava/lang/Object)Z")) { heStatus = HEStatus.NOT_NEEDED; } } else if (!fieldSig.startsWith("Ljava/lang/") && !fieldSig.startsWith("Ljava/util/")) { heStatus = HEStatus.NOT_NEEDED; } } else if (!fieldSig.startsWith("[")) { heStatus = HEStatus.NEEDED; } } } else { heStatus = HEStatus.NOT_NEEDED; } } } if (!clsHasRuntimeAnnotation && (heStatus == HEStatus.NEEDED)) { if (!hasMethodInHierarchy(cls, "equals", "(Ljava/lang/Object;)Z")) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_EQUALS.name(), LOW_PRIORITY) .addClass(cls)); } else if (!hasMethodInHierarchy(cls, "hashCode", "()I")) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_HASHCODE.name(), LOW_PRIORITY) .addClass(cls)); } } } catch (ClassNotFoundException cnfe) { bugReporter.reportMissingClass(cnfe); } } }