docs: @SuppressWarnings("PMD.AvoidDecimalLiteralsInBigDecimalConstructor")
      @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS", justification = "False positive, see https://github.com/spotbugs/spotbugs/issues/87")
      @Override
      public void sawOpcode(int seen) {
  
          if (isMethodCall()) {
              MethodDescriptor called = getMethodDescriptorOperand();
  
              if (previousMethodCall != null && !stack.isJumpTarget(getPC())) {
                  if ("toString".equals(called.getName())
                          && "java/lang/Integer".equals(called.getClassDescriptor().getClassName())
                          && "valueOf".equals(previousMethodCall.getName())
                          && "(I)Ljava/lang/Integer;".equals(previousMethodCall.getSignature())
                          ) {
                      MethodAnnotation preferred = new MethodAnnotation(Values.DOTTED_JAVA_LANG_INTEGER, "toString", "(I)Ljava/lang/String;", true);
                      BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_TOSTRING", HIGH_PRIORITY).addClassAndMethod(this)
                              .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
                      accumulator.accumulateBug(bug, this);
  
                  }  else if ("intValue".equals(called.getName())
                          && "java/lang/Integer".equals(called.getClassDescriptor().getClassName())
                          && "java/lang/Integer".equals(previousMethodCall.getSlashedClassName())
                          && (Const.CONSTRUCTOR_NAME.equals(previousMethodCall.getName())
                                  && "(Ljava/lang/String;)V".equals(previousMethodCall.getSignature())
                                  || "valueOf".equals(previousMethodCall.getName())
                                  && "(Ljava/lang/String;)Ljava/lang/Integer;".equals(previousMethodCall.getSignature())
                                  )) {
  
                      MethodAnnotation preferred = new MethodAnnotation(Values.DOTTED_JAVA_LANG_INTEGER, "parseInt", "(Ljava/lang/String;)I", true);
  
                      BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_PARSING", HIGH_PRIORITY).addClassAndMethod(this)
                              .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
                      accumulator.accumulateBug(bug, this);
                  }  else if ("longValue".equals(called.getName())
                          && "java/lang/Long".equals(called.getClassDescriptor().getClassName())
                          && "java/lang/Long".equals(previousMethodCall.getSlashedClassName())
                          && ( Const.CONSTRUCTOR_NAME.equals(previousMethodCall.getName())
                                  && "(Ljava/lang/String;)V".equals(previousMethodCall.getSignature())
                                  ||  "valueOf".equals(previousMethodCall.getName())
                                  && "(Ljava/lang/String;)Ljava/lang/Long;".equals(previousMethodCall.getSignature()))
                          ) {
                      MethodAnnotation preferred = new MethodAnnotation("java.lang.Long", "parseLong", "(Ljava/lang/String;)J", true);
  
                      BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_PARSING", HIGH_PRIORITY).addClassAndMethod(this)
                              .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
                      accumulator.accumulateBug(bug, this);
                  } else if("compareTo".equals(called.getName())
                          && "valueOf".equals(previousMethodCall.getName())
                          && called.getClassDescriptor().equals(previousMethodCall.getClassDescriptor()) && !previousMethodCall.getSignature().startsWith("(Ljava/lang/String;")
                          ) {
                      String primitiveType = ClassName.getPrimitiveType(called.getClassDescriptor().getClassName());
                      XMethod rvo = stack.getStackItem(1).getReturnValueOf();
                      XField field = stack.getStackItem(1).getXField();
                      String signature;
                      if (rvo != null) {
                          signature = new SignatureParser(rvo.getSignature()).getReturnTypeSignature();
                      } else if (field != null) {
                          signature = field.getSignature();
                      } else {
                          signature = "";
                      }
                      if (primitiveType != null
                              && (previousMethodCall.equals(rvo) || signature.equals(primitiveType))
                              && (getThisClass().getMajor() >= Const.MAJOR_1_7 || getThisClass().getMajor() >= Const.MAJOR_1_4
                              && (primitiveType.equals("D") || primitiveType.equals("F")))) {
                          MethodDescriptor shouldCall = new MethodDescriptor(called.getClassDescriptor().getClassName(), "compare",
                                  "(" + primitiveType + primitiveType + ")I", true);
                          BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_COMPARE",
                                  primitiveType.equals("Z") ? LOW_PRIORITY : primitiveType.equals("B") ? NORMAL_PRIORITY
                                          : HIGH_PRIORITY).addClassAndMethod(this).addCalledMethod(this).addMethod(shouldCall)
                                  .describe(MethodAnnotation.SHOULD_CALL);
                          accumulator.accumulateBug(bug, this);
                      }
                  }
              }
              previousMethodCall = called;
          } else {
              previousMethodCall = null;
          }
  
  
          if (seen == Const.LDC || seen == Const.LDC_W || seen == Const.LDC2_W) {
              Constant c = getConstantRefOperand();
              if (testingEnabled && (c instanceof ConstantInteger && ((ConstantInteger) c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT
                      || c instanceof ConstantLong && ((ConstantLong) c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT)) {
                  BugInstance bug = new BugInstance(this, "TESTING", HIGH_PRIORITY).addClassAndMethod(this)
                          .addString("Did you mean MICROS_PER_DAY").addInt(MICROS_PER_DAY_OVERFLOWED_AS_INT)
                          .describe(IntAnnotation.INT_VALUE);
                  accumulator.accumulateBug(bug, this);
              }
              if ((c instanceof ConstantInteger && ((ConstantInteger) c).getBytes() == Integer.MIN_VALUE
                      || c instanceof ConstantLong && ((ConstantLong) c).getBytes() == Long.MIN_VALUE)) {
                  sawLoadOfMinValue = true;
                  pendingAbsoluteValueBug = null;
                  pendingAbsoluteValueBugSourceLine = null;
                  absoluteValueAccumulator.clearBugs();
              }
          }
  
  
          if (seen == Const.LCMP) {
              OpcodeStack.Item left = stack.getStackItem(1);
              OpcodeStack.Item right = stack.getStackItem(0);
              checkForCompatibleLongComparison(left, right);
              checkForCompatibleLongComparison(right, left);
          }
  
          if (stack.getStackDepth() >= 2) {
              switch (seen) {
              case Const.IF_ICMPEQ:
              case Const.IF_ICMPNE:
              case Const.IF_ICMPLE:
              case Const.IF_ICMPGE:
              case Const.IF_ICMPLT:
              case Const.IF_ICMPGT:
                  OpcodeStack.Item item0 = stack.getStackItem(0);
                  OpcodeStack.Item item1 = stack.getStackItem(1);
                  if (item0.getConstant() instanceof Integer) {
                      OpcodeStack.Item tmp = item0;
                      item0 = item1;
                      item1 = tmp;
                  }
                  Object constant1 = item1.getConstant();
                  XMethod returnValueOf = item0.getReturnValueOf();
                  if (constant1 instanceof Integer
                          && returnValueOf != null
                          && "getYear".equals(returnValueOf.getName())
                          && ("java.util.Date".equals(returnValueOf.getClassName()) || "java.sql.Date".equals(returnValueOf.getClassName()))) {
                      int year = (Integer) constant1;
                      if (testingEnabled && year > 1900) {
                          accumulator.accumulateBug(
                                  new BugInstance(this, "TESTING", HIGH_PRIORITY).addClassAndMethod(this)
                                  .addString("Comparison of getYear does understand that it returns year-1900")
                                  .addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addInt(year)
                                  .describe(IntAnnotation.INT_VALUE), this);
                      }
                  }
                  break;
              default:
                  break;
              }
          }
  
          // System.out.printf("%4d %10s: %s\n", getPC(), Const.getOpcodeName(seen),
          // stack);
          if (seen == Const.IFLT && stack.getStackDepth() > 0 && stack.getStackItem(0).getSpecialKind() == OpcodeStack.Item.SIGNED_BYTE) {
              sawCheckForNonNegativeSignedByte = getPC();
          }
  
          if (pendingAbsoluteValueBug != null) {
              if (opcodesSincePendingAbsoluteValueBug == 0) {
                  opcodesSincePendingAbsoluteValueBug++;
              } else {
                  if (seen == Const.IREM) {
                      OpcodeStack.Item top = stack.getStackItem(0);
                      Object constantValue = top.getConstant();
                      if (constantValue instanceof Number && Util.isPowerOfTwo(((Number) constantValue).intValue())) {
                          pendingAbsoluteValueBug.setPriority(Priorities.LOW_PRIORITY);
                      }
                  }
                  /*
                  if (false)
                      try {
                          pendingAbsoluteValueBug.addString(OPCODE_NAMES[getPrevOpcode(1)] + ":" + Const.getOpcodeName(seen) + ":"
                                  + OPCODE_NAMES[getNextOpcode()]);
                      } catch (Exception e) {
                          pendingAbsoluteValueBug.addString(OPCODE_NAMES[getPrevOpcode(1)] + ":" + Const.getOpcodeName(seen));
  
                      }
                   */
                  absoluteValueAccumulator.accumulateBug(pendingAbsoluteValueBug, pendingAbsoluteValueBugSourceLine);
                  pendingAbsoluteValueBug = null;
                  pendingAbsoluteValueBugSourceLine = null;
              }
          }
  
          if (seen == Const.INVOKESTATIC
                  && "org/easymock/EasyMock".equals(getClassConstantOperand())
                  && ("replay".equals(getNameConstantOperand()) || "verify".equals(getNameConstantOperand()) || getNameConstantOperand()
                          .startsWith("reset")) && "([Ljava/lang/Object;)V".equals(getSigConstantOperand())
                  && getPrevOpcode(1) == Const.ANEWARRAY && getPrevOpcode(2) == Const.ICONST_0) {
              accumulator.accumulateBug(new BugInstance(this, "DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD", NORMAL_PRIORITY)
                      .addClassAndMethod(this).addCalledMethod(this), this);
          }
  
          if ((seen == Const.INVOKESTATIC || seen == Const.INVOKEVIRTUAL || seen == Const.INVOKESPECIAL || seen == Const.INVOKEINTERFACE)
                  && getSigConstantOperand().indexOf("Ljava/lang/Runnable;") >= 0) {
              SignatureParser parser = new SignatureParser(getSigConstantOperand());
              int count = 0;
              for (Iterator<String> i = parser.parameterSignatureIterator(); i.hasNext(); count++) {
                  String parameter = i.next();
                  if ("Ljava/lang/Runnable;".equals(parameter)) {
                      OpcodeStack.Item item = stack.getStackItem(parser.getNumParameters() - 1 - count);
                      if ("Ljava/lang/Thread;".equals(item.getSignature())) {
                          accumulator.accumulateBug(new BugInstance(this, "DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED",
                                  NORMAL_PRIORITY).addClassAndMethod(this).addCalledMethod(this), this);
                      }
  
                  }
              }
  
          }
  
          if (prevOpcode == Const.I2L && seen == Const.INVOKESTATIC && "java/lang/Double".equals(getClassConstantOperand())
                  && "longBitsToDouble".equals(getNameConstantOperand())) {
              accumulator.accumulateBug(new BugInstance(this, "DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT", HIGH_PRIORITY)
                      .addClassAndMethod(this).addCalledMethod(this), this);
          }
  
          /**
           * Since you can change the number of core threads for a scheduled
           * thread pool executor, disabling this for now
           *
          if (false && seen == INVOKESPECIAL
                  && getClassConstantOperand().equals("java/util/concurrent/ScheduledThreadPoolExecutor")
                  && getNameConstantOperand().equals(Const.CONSTRUCTOR_NAME)) {
  
              int arguments = getNumberArguments(getSigConstantOperand());
              OpcodeStack.Item item = stack.getStackItem(arguments - 1);
              Object value = item.getConstant();
              if (value instanceof Integer && ((Integer) value).intValue() == 0)
                  accumulator.accumulateBug(new BugInstance(this, "DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS",
                          HIGH_PRIORITY).addClassAndMethod(this), this);
  
          }
           */
          for(SubDetector subDetector : subDetectors) {
              subDetector.sawOpcode(seen);
          }
  
          if (!sawLoadOfMinValue && seen == Const.INVOKESTATIC &&
                  ClassName.isMathClass(getClassConstantOperand()) && "abs".equals(getNameConstantOperand())
                  ) {
              OpcodeStack.Item item0 = stack.getStackItem(0);
              int special = item0.getSpecialKind();
              if (special == OpcodeStack.Item.RANDOM_INT) {
                  pendingAbsoluteValueBug = new BugInstance(this, "RV_ABSOLUTE_VALUE_OF_RANDOM_INT", HIGH_PRIORITY)
                          .addClassAndMethod(this);
                  pendingAbsoluteValueBugSourceLine = SourceLineAnnotation.fromVisitedInstruction(this);
                  opcodesSincePendingAbsoluteValueBug = 0;
              }
  
              else if (special == OpcodeStack.Item.HASHCODE_INT) {
                  pendingAbsoluteValueBug = new BugInstance(this, "RV_ABSOLUTE_VALUE_OF_HASHCODE", HIGH_PRIORITY)
                          .addClassAndMethod(this);
                  pendingAbsoluteValueBugSourceLine = SourceLineAnnotation.fromVisitedInstruction(this);
                  opcodesSincePendingAbsoluteValueBug = 0;
              }
  
          }
  
          try {
              int stackLoc = stackEntryThatMustBeNonnegative(seen);
              if (stackLoc >= 0) {
                  OpcodeStack.Item tos = stack.getStackItem(stackLoc);
                  switch (tos.getSpecialKind()) {
                  case OpcodeStack.Item.HASHCODE_INT_REMAINDER:
                      accumulator.accumulateBug(new BugInstance(this, "RV_REM_OF_HASHCODE", HIGH_PRIORITY).addClassAndMethod(this),
                              this);
  
                      break;
                  case OpcodeStack.Item.RANDOM_INT:
                  case OpcodeStack.Item.RANDOM_INT_REMAINDER:
                      accumulator.accumulateBug(
                              new BugInstance(this, "RV_REM_OF_RANDOM_INT", HIGH_PRIORITY).addClassAndMethod(this), this);
                      break;
                  default:
                      break;
                  }
  
              }
              if (seen == Const.IREM) {
                  OpcodeStack.Item item0 = stack.getStackItem(0);
                  Object constant0 = item0.getConstant();
                  if (constant0 instanceof Integer && ((Integer) constant0).intValue() == 1) {
                      accumulator.accumulateBug(new BugInstance(this, "INT_BAD_REM_BY_1", HIGH_PRIORITY).addClassAndMethod(this),
                              this);
                  }
  
              }
  
              if (stack.getStackDepth() >= 1 && (seen == Const.LOOKUPSWITCH || seen == Const.TABLESWITCH)) {
                  OpcodeStack.Item item0 = stack.getStackItem(0);
                  if (item0.getSpecialKind() == OpcodeStack.Item.SIGNED_BYTE) {
                      int[] switchLabels = getSwitchLabels();
                      int[] switchOffsets = getSwitchOffsets();
                      for (int i = 0; i < switchLabels.length; i++) {
                          int v = switchLabels[i];
                          if (v <= -129 || v >= 128) {
                              accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_SIGNED_BYTE", HIGH_PRIORITY)
                                      .addClassAndMethod(this).addInt(v).describe(IntAnnotation.INT_VALUE),
                                      SourceLineAnnotation.fromVisitedInstruction(this, getPC() + switchOffsets[i]));
                          }
  
                      }
                  }
              }
              // check for use of signed byte where is it assumed it can be out of
              // the -128...127 range
              if (stack.getStackDepth() >= 2) {
                  switch (seen) {
                  case Const.IF_ICMPEQ:
                  case Const.IF_ICMPNE:
                  case Const.IF_ICMPLT:
                  case Const.IF_ICMPLE:
                  case Const.IF_ICMPGE:
                  case Const.IF_ICMPGT:
                      OpcodeStack.Item item0 = stack.getStackItem(0);
                      OpcodeStack.Item item1 = stack.getStackItem(1);
                      int seen2 = seen;
                      if (item0.getConstant() != null) {
                          OpcodeStack.Item tmp = item0;
                          item0 = item1;
                          item1 = tmp;
                          switch (seen) {
                          case Const.IF_ICMPLT:
                              seen2 = Const.IF_ICMPGT;
                              break;
                          case Const.IF_ICMPGE:
                              seen2 = Const.IF_ICMPLE;
                              break;
                          case Const.IF_ICMPGT:
                              seen2 = Const.IF_ICMPLT;
                              break;
                          case Const.IF_ICMPLE:
                              seen2 = Const.IF_ICMPGE;
                              break;
                          default:
                              break;
                          }
                      }
                      Object constant1 = item1.getConstant();
                      if (item0.getSpecialKind() == OpcodeStack.Item.SIGNED_BYTE && constant1 instanceof Number) {
                          int v1 = ((Number) constant1).intValue();
                          if (v1 <= -129 || v1 >= 128 || v1 == 127 && !(seen2 == Const.IF_ICMPEQ || seen2 == Const.IF_ICMPNE
  
                                  )) {
                              int priority = HIGH_PRIORITY;
                              if (v1 == 127) {
                                  switch (seen2) {
                                  case Const.IF_ICMPGT: // 127 > x
                                      priority = LOW_PRIORITY;
                                      break;
                                  case Const.IF_ICMPGE: // 127 >= x : always true
                                      priority = NORMAL_PRIORITY;
                                      break;
                                  case Const.IF_ICMPLT: // 127 < x : never true
                                      priority = NORMAL_PRIORITY;
                                      break;
                                  case Const.IF_ICMPLE: // 127 <= x
                                      priority = LOW_PRIORITY;
                                      break;
                                  }
                              } else if (v1 == 128) {
                                  switch (seen2) {
                                  case Const.IF_ICMPGT: // 128 > x; always true
                                      priority = NORMAL_PRIORITY;
                                      break;
                                  case Const.IF_ICMPGE: // 128 >= x
                                      priority = HIGH_PRIORITY;
                                      break;
                                  case Const.IF_ICMPLT: // 128 < x
                                      priority = HIGH_PRIORITY;
                                      break;
                                  case Const.IF_ICMPLE: // 128 <= x; never true
                                      priority = NORMAL_PRIORITY;
                                      break;
                                  }
                              } else if (v1 <= -129) {
                                  priority = NORMAL_PRIORITY;
                              }
  
                              if (getPC() - sawCheckForNonNegativeSignedByte < 10) {
                                  priority++;
                              }
  
                              accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_SIGNED_BYTE", priority)
                                      .addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE).addValueSource(item0, this), this);
  
                          }
                      } else if (item0.getSpecialKind() == OpcodeStack.Item.NON_NEGATIVE && constant1 instanceof Number) {
                          int v1 = ((Number) constant1).intValue();
                          if (v1 < 0) {
                              accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE",
                                      HIGH_PRIORITY).addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE).addValueSource(item0, this), this);
                          }
  
                      }
  
                  }
              }
  
              switch (seen) {
              case Const.IFGE:
              case Const.IFLT:
                  if(stack.getStackDepth() > 0 && stack.getStackItem(0).getSpecialKind() == OpcodeStack.Item.NON_NEGATIVE) {
                      OpcodeStack.Item top = stack.getStackItem(0);
                      if (top.getRegisterNumber() != -1 && getMaxPC() > getNextPC() + 6) {
                          int jump1, jump2;
                          if (seen == Const.IFGE) {
                              jump1 = Const.IF_ICMPLT;
                              jump2 = Const.IF_ICMPLE;
                          } else {
                              jump1 = Const.IF_ICMPGE;
                              jump2 = Const.IF_ICMPGT;
                          }
                          int nextCodeByte0 = getNextCodeByte(0);
                          int loadConstant = 1;
                          if (nextCodeByte0 == Const.ILOAD) {
                              loadConstant = 2;
                          }
                          int nextCodeByte1 = getNextCodeByte(loadConstant);
                          int nextJumpOffset = loadConstant+2;
                          if (nextCodeByte1 == Const.SIPUSH) {
                              nextJumpOffset++;
                          }
                          int nextCodeByteJump = getNextCodeByte(nextJumpOffset);
  
                          if (nextCodeByte0 == getPrevOpcode(1)
                                  && (nextCodeByte1 == Const.BIPUSH || nextCodeByte1 == Const.SIPUSH)
                                  && (Const.IF_ICMPLT <= nextCodeByteJump && nextCodeByteJump <= Const.IF_ICMPLE))
                          {
                              break;
                          }
                      }
                      accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE",
                              NORMAL_PRIORITY).addClassAndMethod(this).addInt(0).describe(IntAnnotation.INT_VALUE).addValueSource(top, this), this);
                  }
                  break;
              case Const.IAND:
              case Const.LAND:
              case Const.IOR:
              case Const.LOR:
              case Const.IXOR:
              case Const.LXOR:
                  long badValue = (seen == Const.IAND || seen == Const.LAND) ? -1 : 0;
                  OpcodeStack.Item rhs = stack.getStackItem(0);
                  OpcodeStack.Item lhs = stack.getStackItem(1);
                  int prevOpcode = getPrevOpcode(1);
                  int prevPrevOpcode = getPrevOpcode(2);
                  if (rhs.hasConstantValue(badValue)
                          && (prevOpcode == Const.LDC || prevOpcode == Const.ICONST_0 || prevOpcode == Const.ICONST_M1 || prevOpcode == Const.LCONST_0)
                          && prevPrevOpcode != Const.GOTO) {
                      reportVacuousBitOperation(seen, lhs);
                  }
  
              }
  
              if (checkForBitIorofSignedByte && seen != Const.I2B) {
                  String pattern = (prevOpcode == Const.LOR || prevOpcode == Const.IOR) ? "BIT_IOR_OF_SIGNED_BYTE" : "BIT_ADD_OF_SIGNED_BYTE";
                  int priority = (prevOpcode == Const.LOR || prevOpcode == Const.LADD) ? HIGH_PRIORITY : NORMAL_PRIORITY;
                  accumulator.accumulateBug(new BugInstance(this, pattern, priority).addClassAndMethod(this), this);
  
                  checkForBitIorofSignedByte = false;
              } else if ((seen == Const.IOR || seen == Const.LOR || seen == Const.IADD || seen == Const.LADD) && stack.getStackDepth() >= 2) {
                  OpcodeStack.Item item0 = stack.getStackItem(0);
                  OpcodeStack.Item item1 = stack.getStackItem(1);
  
                  int special0 = item0.getSpecialKind();
                  int special1 = item1.getSpecialKind();
                  if (special0 == OpcodeStack.Item.SIGNED_BYTE && special1 == OpcodeStack.Item.LOW_8_BITS_CLEAR
                          && !item1.hasConstantValue(256) || special0 == OpcodeStack.Item.LOW_8_BITS_CLEAR
                          && !item0.hasConstantValue(256) && special1 == OpcodeStack.Item.SIGNED_BYTE) {
                      checkForBitIorofSignedByte = true;
                  } else {
                      checkForBitIorofSignedByte = false;
                  }
              } else {
                  checkForBitIorofSignedByte = false;
              }
  
              if (prevOpcodeWasReadLine && sinceBufferedInputStreamReady >= 100 && seen == Const.INVOKEVIRTUAL
                      && "java/lang/String".equals(getClassConstantOperand()) && getSigConstantOperand().startsWith("()")) {
                  accumulator.accumulateBug(
                          new BugInstance(this, "NP_IMMEDIATE_DEREFERENCE_OF_READLINE", NORMAL_PRIORITY).addClassAndMethod(this),
                          this);
              }
  
              if (seen == Const.INVOKEVIRTUAL && "java/io/BufferedReader".equals(getClassConstantOperand())
                      && "ready".equals(getNameConstantOperand()) && "()Z".equals(getSigConstantOperand())) {
                  sinceBufferedInputStreamReady = 0;
              } else {
                  sinceBufferedInputStreamReady++;
              }
  
              prevOpcodeWasReadLine = (seen == Const.INVOKEVIRTUAL || seen == Const.INVOKEINTERFACE)
                      && "readLine".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand());
  
              // System.out.println(randomNextIntState + " " + Const.getOpcodeName(seen)
              // + " " + getMethodName());
              switch (randomNextIntState) {
              case 0:
                  if (seen == Const.INVOKEVIRTUAL && "java/util/Random".equals(getClassConstantOperand())
                  && "nextDouble".equals(getNameConstantOperand()) || seen == Const.INVOKESTATIC
                  && ClassName.isMathClass(getClassConstantOperand()) && "random".equals(getNameConstantOperand())) {
                      randomNextIntState = 1;
                  }
                  break;
              case 1:
                  if (seen == Const.D2I) {
                      accumulator.accumulateBug(new BugInstance(this, "RV_01_TO_INT", HIGH_PRIORITY).addClassAndMethod(this), this);
                      randomNextIntState = 0;
                  } else if (seen == Const.DMUL) {
                      randomNextIntState = 4;
                  } else if (seen == Const.LDC2_W && getConstantRefOperand() instanceof ConstantDouble
                          && ((ConstantDouble) getConstantRefOperand()).getBytes() == Integer.MIN_VALUE) {
                      randomNextIntState = 0;
                  } else {
                      randomNextIntState = 2;
                  }
  
                  break;
              case 2:
                  if (seen == Const.I2D) {
                      randomNextIntState = 3;
                  } else if (seen == Const.DMUL) {
                      randomNextIntState = 4;
                  } else {
                      randomNextIntState = 0;
                  }
                  break;
              case 3:
                  if (seen == Const.DMUL) {
                      randomNextIntState = 4;
                  } else {
                      randomNextIntState = 0;
                  }
                  break;
              case 4:
                  if (seen == Const.D2I) {
                      accumulator.accumulateBug(
                              new BugInstance(this, "DM_NEXTINT_VIA_NEXTDOUBLE", NORMAL_PRIORITY).addClassAndMethod(this), this);
                  }
                  randomNextIntState = 0;
                  break;
              default:
                  throw new IllegalStateException();
              }
              if (isPublicStaticVoidMain
                      && seen == Const.INVOKEVIRTUAL
                      && getClassConstantOperand().startsWith("javax/swing/")
                      && ("show".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())
                              || "pack".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand()) || "setVisible".equals(getNameConstantOperand()) && "(Z)V".equals(getSigConstantOperand()))) {
                  accumulator.accumulateBug(
                          new BugInstance(this, "SW_SWING_METHODS_INVOKED_IN_SWING_THREAD", LOW_PRIORITY).addClassAndMethod(this),
                          this);
              }
  
              if ((seen == Const.INVOKEVIRTUAL) && "isAnnotationPresent".equals(getNameConstantOperand())
                      && "(Ljava/lang/Class;)Z".equals(getSigConstantOperand()) && stack.getStackDepth() > 0) {
                  OpcodeStack.Item item = stack.getStackItem(0);
                  Object value = item.getConstant();
                  if (value instanceof String) {
                      String annotationClassName = (String) value;
                      boolean lacksClassfileRetention = AnalysisContext.currentAnalysisContext().getAnnotationRetentionDatabase()
                              .lacksRuntimeRetention(annotationClassName.replace('/', '.'));
                      if (lacksClassfileRetention) {
                          ClassDescriptor annotationClass = DescriptorFactory.createClassDescriptor(annotationClassName);
                          accumulator.accumulateBug(
                                  new BugInstance(this, "DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION", HIGH_PRIORITY)
                                  .addClassAndMethod(this).addCalledMethod(this).addClass(annotationClass)
                                  .describe(ClassAnnotation.ANNOTATION_ROLE), this);
                      }
  
                  }
  
              }
              if ((seen == Const.INVOKEVIRTUAL) && "next".equals(getNameConstantOperand())
                      && "()Ljava/lang/Object;".equals(getSigConstantOperand()) && "hasNext".equals(getMethodName())
                      && "()Z".equals(getMethodSig()) && stack.getStackDepth() > 0) {
                  OpcodeStack.Item item = stack.getStackItem(0);
  
                  accumulator.accumulateBug(new BugInstance(this, "DMI_CALLING_NEXT_FROM_HASNEXT", item.isInitialParameter()
                          && item.getRegisterNumber() == 0 ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this)
                          .addCalledMethod(this), this);
  
              }
  
              if ((seen == Const.INVOKESPECIAL) && "java/lang/String".equals(getClassConstantOperand())
                      && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && "(Ljava/lang/String;)V".equals(getSigConstantOperand())
                      && !Subtypes2.isJSP(getThisClass())) {
  
                  accumulator.accumulateBug(new BugInstance(this, "DM_STRING_CTOR", NORMAL_PRIORITY).addClassAndMethod(this), this);
  
              }
  
              if (seen == Const.INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())
                      && "runFinalizersOnExit".equals(getNameConstantOperand()) || seen == Const.INVOKEVIRTUAL
                      && "java/lang/Runtime".equals(getClassConstantOperand())
                      && "runFinalizersOnExit".equals(getNameConstantOperand())) {
                  accumulator.accumulateBug(
                          new BugInstance(this, "DM_RUN_FINALIZERS_ON_EXIT", HIGH_PRIORITY).addClassAndMethod(this), this);
              }
  
              if ((seen == Const.INVOKESPECIAL) && "java/lang/String".equals(getClassConstantOperand())
                      && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())) {
  
                  accumulator.accumulateBug(new BugInstance(this, "DM_STRING_VOID_CTOR", NORMAL_PRIORITY).addClassAndMethod(this),
                          this);
  
              }
  
              if (!isPublicStaticVoidMain && seen == Const.INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())
                      && "exit".equals(getNameConstantOperand()) && !"processWindowEvent".equals(getMethodName())
                      && !getMethodName().startsWith("windowClos") && getMethodName().indexOf("exit") == -1
                      && getMethodName().indexOf("Exit") == -1 && getMethodName().indexOf("crash") == -1
                      && getMethodName().indexOf("Crash") == -1 && getMethodName().indexOf("die") == -1
                      && getMethodName().indexOf("Die") == -1 && getMethodName().indexOf("main") == -1) {
                  accumulator.accumulateBug(new BugInstance(this, "DM_EXIT", getMethod().isStatic() ? LOW_PRIORITY
                          : NORMAL_PRIORITY).addClassAndMethod(this), SourceLineAnnotation.fromVisitedInstruction(this));
              }
              if (((seen == Const.INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())) || (seen == Const.INVOKEVIRTUAL && "java/lang/Runtime".equals(getClassConstantOperand())))
                      && "gc".equals(getNameConstantOperand())
                      && "()V".equals(getSigConstantOperand())
                      && !getDottedClassName().startsWith("java.lang")
                      && !getMethodName().startsWith("gc") && !getMethodName().endsWith("gc")) {
                  if (gcInvocationBugReport == null) {
                      // System.out.println("Saw call to GC");
                      if (isPublicStaticVoidMain) {
                          // System.out.println("Skipping GC complaint in main method");
                          return;
                      }
                      if (isTestMethod(getMethod())) {
                          return;
                      }
                      // Just save this report in a field; it will be flushed
                      // IFF there were no calls to System.currentTimeMillis();
                      // in the method.
                      gcInvocationBugReport = new BugInstance(this, "DM_GC", HIGH_PRIORITY).addClassAndMethod(this).addSourceLine(
                              this);
                      gcInvocationPC = getPC();
                      // System.out.println("GC invocation at pc " + PC);
                  }
              }
              if (!isSynthetic && (seen == Const.INVOKESPECIAL) && "java/lang/Boolean".equals(getClassConstantOperand())
                      && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && !"java/lang/Boolean".equals(getClassName())) {
                  int majorVersion = getThisClass().getMajor();
                  if (majorVersion >= Const.MAJOR_1_4) {
                      accumulator.accumulateBug(new BugInstance(this, "DM_BOOLEAN_CTOR", NORMAL_PRIORITY).addClassAndMethod(this),
                              this);
                  }
  
              }
              if ((seen == Const.INVOKESTATIC) && "java/lang/System".equals(getClassConstantOperand())
                      && ("currentTimeMillis".equals(getNameConstantOperand()) || "nanoTime".equals(getNameConstantOperand()))) {
                  sawCurrentTimeMillis = true;
              }
              if ((seen == Const.INVOKEVIRTUAL) && "java/lang/String".equals(getClassConstantOperand())
                      && "toString".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
  
                  accumulator
                  .accumulateBug(new BugInstance(this, "DM_STRING_TOSTRING", LOW_PRIORITY).addClassAndMethod(this), this);
  
              }
  
              if ((seen == Const.INVOKEVIRTUAL) && "java/lang/String".equals(getClassConstantOperand())
                      && ("toUpperCase".equals(getNameConstantOperand()) || "toLowerCase".equals(getNameConstantOperand()))
                      && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
  
                  accumulator.accumulateBug(new BugInstance(this, "DM_CONVERT_CASE", LOW_PRIORITY).addClassAndMethod(this), this);
  
              }
  
              if ((seen == Const.INVOKESPECIAL) && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand())) {
                  String cls = getClassConstantOperand();
                  String sig = getSigConstantOperand();
                  String primitiveType = ClassName.getPrimitiveType(cls);
                  if (primitiveType != null && sig.charAt(1) == primitiveType.charAt(0)) {
                      primitiveObjCtorSeen = cls;
                  } else {
                      primitiveObjCtorSeen = null;
                  }
              } else if ((primitiveObjCtorSeen != null) && (seen == Const.INVOKEVIRTUAL) && "toString".equals(getNameConstantOperand())
                      && getClassConstantOperand().equals(primitiveObjCtorSeen)
                      && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
                  BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_TOSTRING", NORMAL_PRIORITY).addClassAndMethod(this).addCalledMethod(this);
                  MethodAnnotation preferred = new MethodAnnotation(ClassName.toDottedClassName(primitiveObjCtorSeen),
                          "toString", "("+ClassName.getPrimitiveType(primitiveObjCtorSeen)+")Ljava/lang/String;", true);
                  bug.addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
                  accumulator.accumulateBug(
                          bug, this);
  
                  primitiveObjCtorSeen = null;
              } else {
                  primitiveObjCtorSeen = null;
              }
  
              if ((seen == Const.INVOKESPECIAL) && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand())) {
                  ctorSeen = true;
              } else if (ctorSeen && (seen == Const.INVOKEVIRTUAL) && "java/lang/Object".equals(getClassConstantOperand())
                      && "getClass".equals(getNameConstantOperand()) && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
                  accumulator.accumulateBug(new BugInstance(this, "DM_NEW_FOR_GETCLASS", NORMAL_PRIORITY).addClassAndMethod(this),
                          this);
                  ctorSeen = false;
              } else {
                  ctorSeen = false;
              }
  
              if ((seen == Const.INVOKEVIRTUAL) && isMonitorWait(getNameConstantOperand(), getSigConstantOperand())) {
                  checkMonitorWait();
              }
  
              if ((seen == Const.INVOKESPECIAL) && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand())
                      && "java/lang/Thread".equals(getClassConstantOperand())) {
                  String sig = getSigConstantOperand();
                  if ("()V".equals(sig) || "(Ljava/lang/String;)V".equals(sig)
                          || "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V".equals(sig)) {
                      OpcodeStack.Item invokedOn = stack.getItemMethodInvokedOn(this);
                      if (!Const.CONSTRUCTOR_NAME.equals(getMethodName()) || invokedOn.getRegisterNumber() != 0) {
                          accumulator.accumulateBug(
                                  new BugInstance(this, "DM_USELESS_THREAD", LOW_PRIORITY).addClassAndMethod(this), this);
  
                      }
                  }
              }
  
              if (seen == Const.INVOKESPECIAL && "java/math/BigDecimal".equals(getClassConstantOperand())
                      && Const.CONSTRUCTOR_NAME.equals(getNameConstantOperand()) && "(D)V".equals(getSigConstantOperand())) {
                  OpcodeStack.Item top = stack.getStackItem(0);
                  Object value = top.getConstant();
                  if (value instanceof Double && !((Double)value).isInfinite() && !((Double)value).isNaN()) {
                      double arg = ((Double) value).doubleValue();
                      String dblString = Double.toString(arg);
                      String bigDecimalString = new BigDecimal(arg).toString();
                      boolean ok = dblString.equals(bigDecimalString) || dblString.equals(bigDecimalString + ".0");
  
                      if (!ok) {
                          boolean scary = dblString.length() <= 8 && bigDecimalString.length() > 12
                                  && dblString.toUpperCase().indexOf('E') == -1;
                          bugReporter.reportBug(new BugInstance(this, "DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE",
                                  scary ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this).addCalledMethod(this)
                                  .addMethod("java.math.BigDecimal", "valueOf", "(D)Ljava/math/BigDecimal;", true)
                                  .describe(MethodAnnotation.METHOD_ALTERNATIVE_TARGET).addString(dblString)
                                  .addString(bigDecimalString).addSourceLine(this));
                      }
                  }
  
              }
  
          } finally {
              prevOpcode = seen;
          }
      }