private void sawPutField() {
   if (stack.getStackDepth() > 1) {
     OpcodeStack.Item item = stack.getStackItem(0);
     Object uo = item.getUserValue();
     if ((uo != null) && !(uo instanceof Boolean)) {
       clearUserValue(item);
     }
   }
 }
 /**
  * Checks to see if any of the locals or fields that we are tracking are passed into another
  * method. If they are, we clear out our tracking of them, because we can't easily track their
  * progress into the method.
  *
  * <p>This can be overridden to check for exceptions to this rule, for example, being logged to
  * the console not counting.
  */
 protected void processMethodParms() {
   String sig = getSigConstantOperand();
   int numParms = SignatureUtils.getNumParameters(sig);
   if ((numParms > 0) && (stack.getStackDepth() >= numParms)) {
     for (int i = 0; i < numParms; i++) {
       clearUserValue(stack.getStackItem(i));
     }
   }
 }
 private Object sawGetField(Object userObject) {
   if (stack.getStackDepth() > 0) {
     OpcodeStack.Item item = stack.getStackItem(0);
     String sig = item.getSignature();
     if ((item.getRegisterNumber() == 0) || ((sig != null) && sig.equals(clsSignature))) {
       return sawGetStatic(userObject);
     }
   }
   return userObject;
 }
 private void handleTernary(int seen) {
   if (((seen == GETFIELD) || (seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3)))
       && (stack.getStackDepth() > 0)) {
     OpcodeStack.Item item = stack.getStackItem(0);
     clearUserValue(item);
   }
   /*
    * check ALOAD_0, as if it's a field the statement after a GOTO will be
    * loading 'this'
    */
   if ((seen != GOTO) && (seen != IFNULL) && (seen != IFNONNULL) && (seen != ALOAD_0)) {
     sawTernary = false;
   }
 }
  /**
   * implements the visitor to look for calls to java.utils.Properties.put, where the value is a non
   * String. Reports both cases, where if it is a string, at a lower lever.
   *
   * @param seen the currently parsed op code
   */
  @Override
  public void sawOpcode(int seen) {
    try {
      stack.precomputation(this);

      if (seen == INVOKEVIRTUAL) {
        String clsName = getClassConstantOperand();
        if ("java/util/Properties".equals(clsName)) {
          String methodName = getNameConstantOperand();
          if ("put".equals(methodName)) {
            String sig = getSigConstantOperand();
            if (SignatureBuilder.SIG_TWO_OBJECTS_TO_OBJECT.equals(sig)
                && (stack.getStackDepth() >= 3)) {
              OpcodeStack.Item valueItem = stack.getStackItem(0);
              String valueSig = valueItem.getSignature();
              if (Values.SIG_JAVA_LANG_STRING.equals(valueSig)) {
                bugReporter.reportBug(
                    new BugInstance(
                            this,
                            BugType.IPU_IMPROPER_PROPERTIES_USE_SETPROPERTY.name(),
                            LOW_PRIORITY)
                        .addClass(this)
                        .addMethod(this)
                        .addSourceLine(this));
              } else if (Values.SIG_JAVA_LANG_OBJECT.equals(valueSig)) {
                bugReporter.reportBug(
                    new BugInstance(
                            this,
                            BugType.IPU_IMPROPER_PROPERTIES_USE_SETPROPERTY.name(),
                            NORMAL_PRIORITY)
                        .addClass(this)
                        .addMethod(this)
                        .addSourceLine(this));
              } else {
                bugReporter.reportBug(
                    new BugInstance(
                            this, BugType.IPU_IMPROPER_PROPERTIES_USE.name(), NORMAL_PRIORITY)
                        .addClass(this)
                        .addMethod(this)
                        .addSourceLine(this));
              }
            }
          }
        }
      }
    } finally {
      stack.sawOpcode(this, seen);
    }
  }
  /**
   * implements the visitor to reset the stack and proceed for private methods
   *
   * @param obj the context object of the currently parsed code block
   */
  @Override
  public void visitCode(Code obj) {
    Method m = getMethod();
    int aFlags = m.getAccessFlags();
    if ((((aFlags & Constants.ACC_PRIVATE) != 0) || ((aFlags & Constants.ACC_STATIC) != 0))
        && ((aFlags & Constants.ACC_SYNTHETIC) == 0)
        && (!m.getSignature().endsWith(")Z"))) {
      stack.resetForMethodEntry(this);
      returnRegister = -1;
      returnConstant = null;
      registerConstants.clear();
      methodSuspect = true;
      returnPC = -1;
      super.visitCode(obj);
      if (methodSuspect && (returnConstant != null)) {
        BugInstance bi =
            new BugInstance(
                    this,
                    BugType.MRC_METHOD_RETURNS_CONSTANT.name(),
                    ((aFlags & Constants.ACC_PRIVATE) != 0) ? NORMAL_PRIORITY : LOW_PRIORITY)
                .addClass(this)
                .addMethod(this);
        if (returnPC >= 0) {
          bi.addSourceLine(this, returnPC);
        }

        bi.addString(returnConstant.toString());
        bugReporter.reportBug(bi);
      }
    }
  }
示例#7
0
  @Override
  public void visitCode(Code obj) {
    Method m = getMethod();
    if (m.getReturnType() == Type.VOID) {
      return;
    }

    stack.resetForMethodEntry(this);
    ifBlocks.clear();
    activeUnconditional = null;

    CodeException[] ces = obj.getExceptionTable();
    if (CollectionUtils.isEmpty(ces)) {
      catchPCs = null;
    } else {
      catchPCs = new BitSet();
      for (CodeException ce : ces) {
        catchPCs.set(ce.getHandlerPC());
      }
    }
    gotoBranchPCs.clear();
    casePositions.clear();
    lookingForResetOp = false;

    try {
      super.visitCode(obj);
    } catch (StopOpcodeParsingException e) {
      // reported an issue, so get out
    }
  }
示例#8
0
 /**
  * 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);
   regValueType.clear();
   state = State.SEEN_NOTHING;
   loopStart = -1;
   loopEnd = -1;
   super.visitCode(obj);
 }
 private void sawInvokeInterfaceVirtual() {
   String sig = getSigConstantOperand();
   int numParms = SignatureUtils.getNumParameters(sig);
   if (stack.getStackDepth() > numParms) {
     OpcodeStack.Item item = stack.getStackItem(numParms);
     Object uo = item.getUserValue();
     if (uo != null) {
       String name = getNameConstantOperand();
       if (isMethodThatShouldBeCalled(name)) {
         clearUserValue(item);
       } else if (!"clone".equals(name)) {
         if ((!Values.SIG_VOID.equals(SignatureUtils.getReturnSignature(sig))) && !nextOpIsPop()) {
           clearUserValue(item);
         }
       }
     }
   }
   processMethodParms();
 }
 private void sawAStore(int seen) {
   int depth = stack.getStackDepth();
   if (depth > 0) {
     OpcodeStack.Item item = stack.getStackItem(0);
     Object uo = item.getUserValue();
     if (uo != null) {
       if (uo instanceof Boolean) {
         int reg = RegisterUtils.getAStoreReg(this, seen);
         localSpecialObjects.put(Integer.valueOf(reg), Integer.valueOf(getPC()));
         if (getPrevOpcode(1) == DUP) {
           item = stack.getStackItem(1);
           item.setUserValue(Integer.valueOf(reg));
         }
       } else {
         clearUserValue(item);
       }
     }
   }
 }
  /**
   * overrides the visitor reset the stack
   *
   * @param obj the context object of the currently parsed code block
   */
  @Override
  public void visitCode(Code obj) {

    stack.resetForMethodEntry(this);
    localSpecialObjects.clear();
    sawTernary = false;
    super.visitCode(obj);

    for (Integer pc : localSpecialObjects.values()) {
      bugReporter.reportBug(
          makeLocalBugInstance().addClass(this).addMethod(this).addSourceLine(this, pc.intValue()));
    }
  }
  /**
   * 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()));
      }
    }
  }
  /**
   * overrides the visitor to reset the stack for the new method, then checks if the immutability
   * field is set to immutable and if so reports it
   *
   * @param obj the context object of the currently parsed method
   */
  @Override
  public void visitCode(Code obj) {
    try {
      String signature = Type.getReturnType(getMethod().getSignature()).getSignature();
      if (signature.startsWith("L")
          && CollectionUtils.isListSetMap(signature.substring(1, signature.length() - 1))) {
        stack.resetForMethodEntry(this);
        imType = ImmutabilityType.UNKNOWN;
        super.visitCode(obj);

        if ((imType == ImmutabilityType.IMMUTABLE)
            || (imType == ImmutabilityType.POSSIBLY_IMMUTABLE)) {
          Method m = getMethod();
          Statistics.getStatistics()
              .addImmutabilityStatus(clsName, m.getName(), m.getSignature(), imType);
        }
      }
    } catch (ClassNotFoundException cnfe) {
      bugReporter.reportMissingClass(cnfe);
    }
  }
  /**
   * implements the visitor to look for methods that return a constant
   *
   * @param seen the opcode of the currently parsed instruction
   */
  @Override
  public void sawOpcode(int seen) {
    boolean sawSBToString = false;
    try {
      if (!methodSuspect) {
        return;
      }

      stack.precomputation(this);
      if ((seen >= IRETURN) && (seen <= ARETURN)) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);

          int register = item.getRegisterNumber();
          if (registerConstants.containsKey(register)
              && (registerConstants.get(register) == null)) {
            methodSuspect = false;
            return;
          }

          Object constant = item.getConstant();
          if (constant == null) {
            methodSuspect = false;
            return;
          }
          if (Boolean.TRUE.equals(item.getUserValue()) && ("".equals(constant))) {
            methodSuspect = false;
            return;
          }
          if ((returnConstant != null) && (!returnConstant.equals(constant))) {
            methodSuspect = false;
            return;
          }

          returnRegister = item.getRegisterNumber();
          returnConstant = constant;
        }
      } else if ((seen == GOTO) || (seen == GOTO_W)) {
        if (stack.getStackDepth() > 0) {
          methodSuspect =
              false; // Trinaries confuse us too much, if the code has a ternary well - oh well
        }
      } else if (seen == INVOKEVIRTUAL) {
        String clsName = getClassConstantOperand();
        if (clsName.startsWith("java/lang/StringB")) {
          sawSBToString = "toString".equals(getNameConstantOperand());
        }
      } else if ((seen >= ISTORE) && (seen <= ASTORE_3) || (seen == IINC)) {
        int register = getRegisterOperand();
        if ((returnRegister != -1) && (register == returnRegister)) {
          methodSuspect = false;
        }

        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          Object constant = item.getConstant();
          if (registerConstants.containsKey(register)) {
            if ((constant == null) || !constant.equals(registerConstants.get(register))) {
              registerConstants.put(register, null);
            }
          } else {
            registerConstants.put(register, constant);
          }
        } else {
          registerConstants.put(register, null);
        }

        if (returnRegister == register) {
          Object constant = registerConstants.get(returnRegister);
          if (constant != null) {
            methodSuspect = false;
          }
        }
      }

    } finally {
      TernaryPatcher.pre(stack, seen);
      stack.sawOpcode(this, seen);
      TernaryPatcher.post(stack, seen);
      if (sawSBToString && (stack.getStackDepth() > 0)) {
        OpcodeStack.Item item = stack.getStackItem(0);
        item.setUserValue(Boolean.TRUE);
      }
    }
  }
示例#15
0
  @Override
  public void sawOpcode(int seen) {

    try {

      int removed = 0;
      if (!ifBlocks.isEmpty()) {
        Iterator<IfBlock> it = ifBlocks.iterator();
        while (it.hasNext()) {
          IfBlock block = it.next();
          if ((getPC() >= block.getEnd())) {
            it.remove();
            removed++;
          }
        }
      }
      if (removed > 1) {
        activeUnconditional = null;
      }

      if (!casePositions.isEmpty() && (casePositions.getFirst().intValue() == getPC())) {
        casePositions.removeFirst();
        activeUnconditional = null;
        lookingForResetOp = true;
      }

      if (lookingForResetOp) {
        if (isResetOp(seen)) {
          lookingForResetOp = false;
        } else {
          return;
        }
      }

      if (isBranch(seen)) {
        if (activeUnconditional != null) {
          activeUnconditional = null;
          if (!ifBlocks.isEmpty()) {
            ifBlocks.removeLast();
          }
          lookingForResetOp = true;
        }

        int target = getBranchTarget();

        if (getBranchOffset() > 0) {
          if ((seen == GOTO) || (seen == GOTO_W)) {
            gotoBranchPCs.set(target);
          } else if ((catchPCs == null) || !catchPCs.get(getNextPC())) {
            ifBlocks.addLast(new IfBlock(getNextPC(), target));
          }
        } else {
          removeLoopBlocks(target);
        }
      } else if (isReturn(seen)) {
        if ((activeUnconditional != null) && !gotoBranchPCs.get(activeUnconditional.getEnd())) {

          int ifSize = activeUnconditional.getEnd() - activeUnconditional.getStart();
          int elseSize = getPC() - activeUnconditional.getEnd();

          double ratio = (double) ifSize / (double) elseSize;
          if (ratio > lowBugRatioLimit) {
            bugReporter.reportBug(
                new BugInstance(
                        this,
                        BugType.BL_BURYING_LOGIC.name(),
                        ratio > normalBugRatioLimit ? NORMAL_PRIORITY : LOW_PRIORITY)
                    .addClass(this)
                    .addMethod(this)
                    .addSourceLineRange(
                        this, activeUnconditional.getStart(), activeUnconditional.getEnd()));
            throw new StopOpcodeParsingException();
          }
        } else if (!ifBlocks.isEmpty()
            && (getNextPC() == ifBlocks.getFirst().getEnd())
            && !gotoAcrossPC(getNextPC())) {
          activeUnconditional = ifBlocks.getFirst();
        }
      } else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) {
        int[] offsets = getSwitchOffsets();
        int pc = getPC();
        for (int offset : offsets) {
          casePositions.addFirst(Integer.valueOf(pc + offset));
        }
      }
    } finally {
      stack.sawOpcode(this, seen);
    }
  }
  /**
   * overrides the visitor to look for calls to static methods that are known to return immutable
   * collections It records those variables, and documents if what the method returns is one of
   * those objects.
   */
  @Override
  public void sawOpcode(int seen) {
    ImmutabilityType seenImmutable = null;
    try {
      stack.precomputation(this);

      switch (seen) {
        case INVOKESTATIC:
          {
            String className = getClassConstantOperand();
            String methodName = getNameConstantOperand();

            if (IMMUTABLE_PRODUCING_METHODS.contains(className + '.' + methodName)) {
              seenImmutable = ImmutabilityType.IMMUTABLE;
              break;
            }
          }
          // $FALL-THROUGH$
        case INVOKEINTERFACE:
        case INVOKESPECIAL:
        case INVOKEVIRTUAL:
          {
            String className = getClassConstantOperand();
            String methodName = getNameConstantOperand();
            String signature = getSigConstantOperand();

            MethodInfo mi =
                Statistics.getStatistics().getMethodStatistics(className, methodName, signature);
            seenImmutable = mi.getImmutabilityType();
            if (seenImmutable == ImmutabilityType.UNKNOWN) seenImmutable = null;
          }
          break;

        case ARETURN:
          {
            if (stack.getStackDepth() > 0) {
              OpcodeStack.Item item = stack.getStackItem(0);
              ImmutabilityType type = (ImmutabilityType) item.getUserValue();
              if (type == null) type = ImmutabilityType.UNKNOWN;

              switch (imType) {
                case UNKNOWN:
                  switch (type) {
                    case IMMUTABLE:
                      imType = ImmutabilityType.IMMUTABLE;
                      break;
                    case POSSIBLY_IMMUTABLE:
                      imType = ImmutabilityType.POSSIBLY_IMMUTABLE;
                      break;
                    default:
                      imType = ImmutabilityType.MUTABLE;
                      break;
                  }
                  break;

                case IMMUTABLE:
                  if (type != ImmutabilityType.IMMUTABLE) {
                    imType = ImmutabilityType.POSSIBLY_IMMUTABLE;
                  }
                  break;

                case POSSIBLY_IMMUTABLE:
                  break;

                case MUTABLE:
                  if (type == ImmutabilityType.IMMUTABLE) {
                    imType = ImmutabilityType.POSSIBLY_IMMUTABLE;
                  }
                  break;
              }
            }
            break;
          }
        default:
          break;
      }

    } finally {
      stack.sawOpcode(this, seen);
      if (seenImmutable != null) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          item.setUserValue(seenImmutable);
        }
      }
    }
  }
 /**
  * implements the visitor to reset the opcode stack
  *
  * @param obj the context object for the currently parsed code block
  */
 @Override
 public void visitCode(Code obj) {
   stack.resetForMethodEntry(this);
   super.visitCode(obj);
 }
示例#18
0
  @Override
  public void sawOpcode(int seen) {
    try {
      stack.precomputation(this);

      int pc = getPC();
      if ((loopEnd != -1) && (pc > loopEnd)) {
        loopStart = -1;
        loopEnd = -1;
        regValueType.clear();
      }

      if ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3))) {
        int reg = RegisterUtils.getALoadReg(this, seen);
        State type = regValueType.get(Integer.valueOf(reg));
        if (type != null) state = type;
        else state = State.SEEN_NOTHING;
        return;
      }
      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);
          regValueType.put(Integer.valueOf(reg), (State) item.getUserValue());
        }
        state = State.SEEN_NOTHING;
        return;
      }
      if ((seen == ILOAD) || ((seen >= ILOAD_0) && (seen <= ILOAD_3))) {
        int reg = RegisterUtils.getLoadReg(this, seen);
        State type = regValueType.get(Integer.valueOf(reg));
        if (type != null) state = type;
        else state = State.SEEN_NOTHING;
        return;
      }
      if ((seen == ISTORE) || ((seen >= ISTORE_0) && (seen <= ISTORE_3))) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          int reg = RegisterUtils.getStoreReg(this, seen);
          regValueType.put(Integer.valueOf(reg), (State) item.getUserValue());
        }
        state = State.SEEN_NOTHING;
        return;
      }

      switch (state) {
        case SEEN_NOTHING:
          if (seen == INVOKESPECIAL) {
            if (("java/util/StringTokenizer".equals(getClassConstantOperand()))
                && (Values.CONSTRUCTOR.equals(getNameConstantOperand()))
                && ("(Ljava/lang/String;Ljava/lang/String;)V".equals(getSigConstantOperand())))
              state = State.SEEN_STRINGTOKENIZER;
          }
          break;

        case SEEN_STRINGTOKENIZER:
          if (seen == INVOKEVIRTUAL) {
            String methodName = getNameConstantOperand();
            String signature = getSigConstantOperand();
            if (("countTokens".equals(methodName)) && ("()I".equals(signature)))
              state = State.SEEN_COUNTTOKENS;
            else if ("hasMoreTokens".equals(methodName) || "hasMoreElements".equals(methodName))
              state = State.SEEN_HASMORE;
            else if ("nextToken".equals(methodName) || "nextElement".equals(methodName)) {
              if ((pc < loopStart) || (pc > loopEnd)) regValueType.clear();
              else state = State.SEEN_NEXT;
            }
          }
          break;

        case SEEN_COUNTTOKENS:
          if (seen == ANEWARRAY) state = State.SEEN_NEWARRAY;
          else if (seen == IF_ICMPGE) {
            int target = getBranchTarget() - 3; // sizeof goto
            byte[] code = getCode().getCode();
            if ((code[target] & 0x000000FF) == GOTO) {
              int offset = (code[target + 1] << 1) + code[target + 2];
              int gotoTarget = target + offset + 3;
              if (gotoTarget < getPC()) {
                loopStart = gotoTarget;
                loopEnd = target;
              }
            }
          }
          break;

        case SEEN_HASMORE:
          if (seen == IFEQ) {
            int target = getBranchTarget() - 3; // sizeof goto
            byte[] code = getCode().getCode();
            if ((code[target] & 0x000000FF) == GOTO) {
              int offset = (code[target + 1] << 1) + code[target + 2];
              int gotoTarget = target + offset + 3;
              if (gotoTarget < getPC()) {
                loopStart = gotoTarget;
                loopEnd = target;
              }
            }
          }
          state = State.SEEN_NOTHING;
          break;

        case SEEN_NEXT:
          if (seen == AASTORE) {
            if ((pc > loopStart) && (pc < loopEnd)) {
              if (stack.getStackDepth() > 2) {
                OpcodeStack.Item arrayItem = stack.getStackItem(2);
                State arrayType = (State) arrayItem.getUserValue();
                OpcodeStack.Item elemItem = stack.getStackItem(0);
                State elemType = (State) elemItem.getUserValue();
                if ((arrayType == State.SEEN_NEWARRAY) && (elemType == State.SEEN_NEXT)) {
                  bugReporter.reportBug(
                      new BugInstance(this, BugType.USS_USE_STRING_SPLIT.name(), NORMAL_PRIORITY)
                          .addClass(this)
                          .addMethod(this)
                          .addSourceLine(this));
                }
              }
            }
          }
          state = State.SEEN_NOTHING;
          break;

        case SEEN_ARRAYSTORE:
        case SEEN_NEWARRAY:
          break;
      }
    } finally {
      TernaryPatcher.pre(stack, seen);
      stack.sawOpcode(this, seen);
      TernaryPatcher.post(stack, seen);
      if (state != State.SEEN_NOTHING) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          item.setUserValue(state);
        }
      }
    }
  }
  /**
   * overrides the visitor to look for uses of collections where the only access to to the
   * collection is to write to it
   *
   * @param seen the opcode of the currently visited instruction
   */
  @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
      value = "SF_SWITCH_FALLTHROUGH",
      justification = "This fall-through is deliberate and documented")
  @Override
  public void sawOpcode(int seen) {
    Object userObject = null;

    // saving and restoring the userobject of the top item, works around a
    // bug in Findbugs proper
    if (stack.getStackDepth() > 0) {
      userObject = stack.getStackItem(0).getUserValue();
    }
    stack.precomputation(this);
    if (stack.getStackDepth() > 0) {
      stack.getStackItem(0).setUserValue(userObject);
      userObject = null;
    }

    try {
      switch (seen) {
        case INVOKESPECIAL:
          userObject = sawInvokeSpecial(userObject);
          break;
        case INVOKEINTERFACE:
        case INVOKEVIRTUAL:
          sawInvokeInterfaceVirtual();
          break;
        case INVOKESTATIC:
          userObject = sawInvokeStatic(userObject);
          // $FALL-THROUGH$
        case INVOKEDYNAMIC:
          processMethodParms();
          break;
        case ARETURN:
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            clearUserValue(item);
          }
          break;

        case ASTORE_0:
        case ASTORE_1:
        case ASTORE_2:
        case ASTORE_3:
        case ASTORE:
          sawAStore(seen);
          break;

        case ALOAD_0:
        case ALOAD_1:
        case ALOAD_2:
        case ALOAD_3:
        case ALOAD:
          userObject = sawLoad(seen, userObject);
          break;

        case AASTORE:
          if (stack.getStackDepth() >= 3) {
            OpcodeStack.Item item = stack.getStackItem(0);
            clearUserValue(item);
          }
          break;

        case PUTFIELD:
          sawPutField();
          break;

        case GETFIELD:
          userObject = sawGetField(userObject);
          break;

        case PUTSTATIC:
          sawPutStatic();
          break;

        case GETSTATIC:
          userObject = sawGetStatic(userObject);
          break;

        case GOTO:
        case IFNULL:
        case IFNONNULL:
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            Object uo = item.getUserValue();
            if ((uo != null) && !(uo instanceof Boolean)) {
              clearUserValue(item);
            }
            sawTernary = true;
          }
          break;
        default:
          break;
      }
    } finally {
      TernaryPatcher.pre(stack, seen);
      stack.sawOpcode(this, seen);
      TernaryPatcher.post(stack, seen);
      if ((userObject != null) && (stack.getStackDepth() > 0)) {
        OpcodeStack.Item item = stack.getStackItem(0);
        item.setUserValue(userObject);
      }
      if (sawTernary) {
        handleTernary(seen);
      }
    }
  }
  /**
   * 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);
        }
      }
    }
  }