@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));
  }