@Override public void sawOpcode(int seen) { if (seen == PUTFIELD) { XField xField = getXFieldOperand(); if (xField != null && xField.getClassDescriptor().equals(getClassDescriptor())) { Item first = stack.getStackItem(0); boolean isPutOfDefaultValue = first.isNull(); // huh?? || first.isInitialParameter(); if (!isPutOfDefaultValue && first.getConstant() != null) { Object constant = first.getConstant(); if (constant instanceof Number && ((Number) constant).intValue() == 0 || constant.equals(Boolean.FALSE)) isPutOfDefaultValue = true; } if (isPutOfDefaultValue) { if (getMethodName().equals("<init>")) transientFieldsSetToDefaultValueInConstructor.add(xField); } else { String nameOfField = getNameConstantOperand(); if (transientFieldsUpdates.containsKey(xField)) { if (getMethodName().equals("<init>")) transientFieldsSetInConstructor.add(xField); else transientFieldsUpdates.put(xField, transientFieldsUpdates.get(xField) + 1); } else if (fieldsThatMightBeAProblem.containsKey(nameOfField)) { try { JavaClass classStored = first.getJavaClass(); if (classStored == null) { return; } double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(classStored); if (isSerializable <= 0.2) { XField f = fieldsThatMightBeAProblem.get(nameOfField); String sig = f.getSignature(); // System.out.println("Field signature: " + sig); // System.out.println("Class stored: " + // classStored.getClassName()); String genSig = "L" + classStored.getClassName().replace('.', '/') + ";"; if (!sig.equals(genSig)) { double bias = 0.0; if (!getMethodName().equals("<init>")) bias = 1.0; int priority = computePriority(isSerializable, bias); fieldWarningList.add( new BugInstance(this, "SE_BAD_FIELD_STORE", priority) .addClass(getThisClass().getClassName()) .addField(f) .addType(genSig) .describe("TYPE_FOUND") .addSourceLine(this)); } } } catch (Exception e) { // ignore it } } } } } }
@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 visitAfter(JavaClass obj) { if (isEnum) return; if (DEBUG) { System.out.println(getDottedClassName()); System.out.println(" hasPublicVoidConstructor: " + hasPublicVoidConstructor); System.out.println(" superClassHasVoidConstructor: " + superClassHasVoidConstructor); System.out.println(" isExternalizable: " + isExternalizable); System.out.println(" isSerializable: " + isSerializable); System.out.println(" isAbstract: " + isAbstract); System.out.println(" superClassImplementsSerializable: " + superClassImplementsSerializable); System.out.println(" isGUIClass: " + isGUIClass); System.out.println(" isEjbImplClass: " + isEjbImplClass); } if (isSerializable && !sawReadObject && !sawReadResolve && seenTransientField && !superClassHasReadObject) { for (Map.Entry<XField, Integer> e : transientFieldsUpdates.entrySet()) { XField fieldX = e.getKey(); int priority = NORMAL_PRIORITY; if (transientFieldsSetInConstructor.contains(e.getKey())) priority--; if (isGUIClass) priority++; if (isEjbImplClass) priority++; if (e.getValue() < 3) priority++; if (transientFieldsSetToDefaultValueInConstructor.contains(e.getKey())) priority++; if (obj.isAbstract()) { priority++; if (priority < Priorities.LOW_PRIORITY) priority = Priorities.LOW_PRIORITY; } try { double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(fieldX.getSignature()); if (isSerializable < 0.6) priority++; } catch (ClassNotFoundException e1) { // ignore it } bugReporter.reportBug( new BugInstance(this, "SE_TRANSIENT_FIELD_NOT_RESTORED", priority) .addClass(getThisClass()) .addField(fieldX)); } } if (isSerializable && !isExternalizable && !superClassHasVoidConstructor && !superClassImplementsSerializable) { int priority = implementsSerializableDirectly || seenTransientField ? HIGH_PRIORITY : (sawSerialVersionUID ? NORMAL_PRIORITY : LOW_PRIORITY); if (isGUIClass) priority++; if (isEjbImplClass) priority++; bugReporter.reportBug( new BugInstance(this, "SE_NO_SUITABLE_CONSTRUCTOR", priority) .addClass(getThisClass().getClassName())); } // Downgrade class-level warnings if it's a GUI or EJB-implementation class. int priority = (isGUIClass || isEjbImplClass) ? LOW_PRIORITY : NORMAL_PRIORITY; if (obj.getClassName().endsWith("_Stub")) priority++; if (isExternalizable && !hasPublicVoidConstructor && !isAbstract) bugReporter.reportBug( new BugInstance( this, "SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION", directlyImplementsExternalizable ? HIGH_PRIORITY : NORMAL_PRIORITY) .addClass(getThisClass().getClassName())); if (!foundSynthetic) priority++; if (seenTransientField) priority--; if (!isAnonymousInnerClass && !isExternalizable && !isGUIClass && !obj.isAbstract() && isSerializable && !isAbstract && !sawSerialVersionUID && !isEjbImplClass) bugReporter.reportBug( new BugInstance(this, "SE_NO_SERIALVERSIONID", priority).addClass(this)); if (writeObjectIsSynchronized && !foundSynchronizedMethods) bugReporter.reportBug( new BugInstance(this, "WS_WRITEOBJECT_SYNC", LOW_PRIORITY).addClass(this)); }