@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; }
@Override public void visit(JavaClass obj) { String superClassname = obj.getSuperclassName(); // System.out.println("superclass of " + getClassName() + " is " + superClassname); isEnum = superClassname.equals("java.lang.Enum"); if (isEnum) return; int flags = obj.getAccessFlags(); isAbstract = (flags & ACC_ABSTRACT) != 0 || (flags & ACC_INTERFACE) != 0; isAnonymousInnerClass = anonymousInnerClassNamePattern.matcher(getClassName()).matches(); innerClassHasOuterInstance = false; for (Field f : obj.getFields()) { if (f.getName().equals("this$0")) { innerClassHasOuterInstance = true; break; } } sawSerialVersionUID = false; isSerializable = implementsSerializableDirectly = false; isExternalizable = false; directlyImplementsExternalizable = false; isGUIClass = false; isEjbImplClass = false; seenTransientField = false; // boolean isEnum = obj.getSuperclassName().equals("java.lang.Enum"); fieldsThatMightBeAProblem.clear(); transientFieldsUpdates.clear(); transientFieldsSetInConstructor.clear(); transientFieldsSetToDefaultValueInConstructor.clear(); // isRemote = false; // Does this class directly implement Serializable? String[] interface_names = obj.getInterfaceNames(); for (String interface_name : interface_names) { if (interface_name.equals("java.io.Externalizable")) { directlyImplementsExternalizable = true; isExternalizable = true; if (DEBUG) { System.out.println("Directly implements Externalizable: " + getClassName()); } } else if (interface_name.equals("java.io.Serializable")) { implementsSerializableDirectly = true; isSerializable = true; if (DEBUG) { System.out.println("Directly implements Serializable: " + getClassName()); } break; } } // Does this class indirectly implement Serializable? if (!isSerializable) { if (Subtypes2.instanceOf(obj, "java.io.Externalizable")) { isExternalizable = true; if (DEBUG) { System.out.println("Indirectly implements Externalizable: " + getClassName()); } } if (Subtypes2.instanceOf(obj, "java.io.Serializable")) { isSerializable = true; if (DEBUG) { System.out.println("Indirectly implements Serializable: " + getClassName()); } } } hasPublicVoidConstructor = false; superClassHasVoidConstructor = true; superClassHasReadObject = false; superClassImplementsSerializable = isSerializable && !implementsSerializableDirectly; ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor(); if (superclassDescriptor != null) try { XClass superXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, superclassDescriptor); if (superXClass != null) { superClassImplementsSerializable = AnalysisContext.currentAnalysisContext() .getSubtypes2() .isSubtype( superXClass.getClassDescriptor(), DescriptorFactory.createClassDescriptor(java.io.Serializable.class)); superClassHasVoidConstructor = false; for (XMethod m : superXClass.getXMethods()) { if (m.getName().equals("<init>") && m.getSignature().equals("()V") && !m.isPrivate()) { superClassHasVoidConstructor = true; } if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V") && m.isPrivate()) superClassHasReadObject = true; } } } catch (ClassNotFoundException e) { bugReporter.reportMissingClass(e); } catch (CheckedAnalysisException e) { bugReporter.logError("huh", e); } // Is this a GUI or other class that is rarely serialized? isGUIClass = false; isEjbImplClass = false; if (true || !directlyImplementsExternalizable && !implementsSerializableDirectly) { isEjbImplClass = Subtypes2.instanceOf(obj, "javax.ejb.SessionBean"); isGUIClass = (Subtypes2.instanceOf(obj, "java.lang.Throwable") || Subtypes2.instanceOf(obj, "java.awt.Component") || Subtypes2.instanceOf(obj, "java.awt.Component$AccessibleAWTComponent") || Subtypes2.instanceOf(obj, "java.awt.event.ActionListener") || Subtypes2.instanceOf(obj, "java.util.EventListener")); if (!isGUIClass) { JavaClass o = obj; while (o != null) { if (o.getClassName().startsWith("java.awt") || o.getClassName().startsWith("javax.swing")) { isGUIClass = true; break; } try { o = o.getSuperClass(); } catch (ClassNotFoundException e) { break; } } } } foundSynthetic = false; foundSynchronizedMethods = false; writeObjectIsSynchronized = false; sawReadExternal = sawWriteExternal = sawReadObject = sawReadResolve = sawWriteObject = false; if (isSerializable) { for (Method m : obj.getMethods()) { if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")) sawReadObject = true; else if (m.getName().equals("readResolve") && m.getSignature().startsWith("()")) sawReadResolve = true; else if (m.getName().equals("readObjectNoData") && m.getSignature().equals("()V")) sawReadObject = true; else if (m.getName().equals("writeObject") && m.getSignature().equals("(Ljava/io/ObjectOutputStream;)V")) sawWriteObject = true; } for (Field f : obj.getFields()) { if (f.isTransient()) seenTransientField = true; } } }