/**
   * implements the visitor to reset the stack
   *
   * @param obj the context object of the currently parsed code block
   */
  @Override
  public void visitCode(Code obj) {
    stack.resetForMethodEntry(this);
    super.visitCode(obj);

    for (Map.Entry<Integer, CollectionRegInfo> entry : syncRegs.entrySet()) {
      CollectionRegInfo cri = entry.getValue();
      if (!cri.getIgnore()) {
        bugReporter.reportBug(
            new BugInstance(this, "LSYC_LOCAL_SYNCHRONIZED_COLLECTION", cri.getPriority())
                .addClass(this)
                .addMethod(this)
                .addSourceLine(cri.getSourceLineAnnotation()));
      }
    }
  }
  /**
   * implements the visitor to find stores to locals of synchronized collections
   *
   * @param seen the opcode of the currently parsed instruction
   */
  @Override
  public void sawOpcode(int seen) {
    Integer tosIsSyncColReg = null;
    try {
      stack.mergeJumps(this);

      if (seen == INVOKESPECIAL) {
        if ("<init>".equals(getNameConstantOperand())) {
          Integer minVersion = syncCtors.get(getClassConstantOperand());
          if ((minVersion != null) && (classVersion >= minVersion.intValue())) {
            tosIsSyncColReg = Integer.valueOf(-1);
          }
        }
      } else if (seen == INVOKESTATIC) {
        if ("java/util/Collections".equals(getClassConstantOperand())) {
          if (syncMethods.contains(getNameConstantOperand())) {
            tosIsSyncColReg = Integer.valueOf(-1);
          }
        }
      } else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          int reg = RegisterUtils.getAStoreReg(this, seen);
          if (item.getUserValue() != null) {
            if (!syncRegs.containsKey(Integer.valueOf(reg))) {
              CollectionRegInfo cri =
                  new CollectionRegInfo(
                      SourceLineAnnotation.fromVisitedInstruction(this),
                      RegisterUtils.getLocalVariableEndRange(
                          getMethod().getLocalVariableTable(), reg, getNextPC()));
              syncRegs.put(Integer.valueOf(reg), cri);
            }
          } else {
            CollectionRegInfo cri = syncRegs.get(Integer.valueOf(reg));
            if (cri == null) {
              cri =
                  new CollectionRegInfo(
                      RegisterUtils.getLocalVariableEndRange(
                          getMethod().getLocalVariableTable(), reg, getNextPC()));
              syncRegs.put(Integer.valueOf(reg), cri);
            }
            cri.setIgnore();
          }
        }
      } else if ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3))) {
        int reg = RegisterUtils.getALoadReg(this, seen);
        CollectionRegInfo cri = syncRegs.get(Integer.valueOf(reg));
        if ((cri != null) && !cri.getIgnore()) tosIsSyncColReg = Integer.valueOf(reg);
      } else if ((seen == PUTFIELD) || (seen == ARETURN)) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          syncRegs.remove(item.getUserValue());
        }
      }

      if (syncRegs.size() > 0) {
        if ((seen == INVOKESPECIAL)
            || (seen == INVOKEINTERFACE)
            || (seen == INVOKEVIRTUAL)
            || (seen == INVOKESTATIC)) {
          String sig = getSigConstantOperand();
          int argCount = Type.getArgumentTypes(sig).length;
          if (stack.getStackDepth() >= argCount) {
            for (int i = 0; i < argCount; i++) {
              OpcodeStack.Item item = stack.getStackItem(i);
              CollectionRegInfo cri = syncRegs.get(item.getUserValue());
              if (cri != null) cri.setPriority(LOW_PRIORITY);
            }
          }
        } else if (seen == MONITORENTER) {
          // Assume if synchronized blocks are used then something tricky is going on.
          // There is really no valid reason for this, other than folks who use
          // synchronized blocks tend to know what's going on.
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            syncRegs.remove(item.getUserValue());
          }
        } else if (seen == AASTORE) {
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            syncRegs.remove(item.getUserValue());
          }
        }
      }

      int curPC = getPC();
      Iterator<CollectionRegInfo> it = syncRegs.values().iterator();
      while (it.hasNext()) {
        CollectionRegInfo cri = it.next();
        if (cri.getEndPCRange() < curPC) {
          if (!cri.getIgnore()) {
            bugReporter.reportBug(
                new BugInstance(this, "LSYC_LOCAL_SYNCHRONIZED_COLLECTION", cri.getPriority())
                    .addClass(this)
                    .addMethod(this)
                    .addSourceLine(cri.getSourceLineAnnotation()));
          }
          it.remove();
        }
      }
    } finally {
      TernaryPatcher.pre(stack, seen);
      stack.sawOpcode(this, seen);
      TernaryPatcher.post(stack, seen);
      if (tosIsSyncColReg != null) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          item.setUserValue(tosIsSyncColReg);
        }
      }
    }
  }