implements the visitor to find methods that employ tail recursion

@param seen
the opcode of the currently parsed instruction
517443
@Override
    public void sawOpcode(int seen) {
        try {
            stack.precomputation(this);

            if (seen == Const.INVOKEVIRTUAL) {
                checkForTailRecursion();
            }
        } finally {
            stack.sawOpcode(this, seen);
        }
    }
268364
@Override
    public void sawOpcode(final int seen) {
        if (stage == Stage.FIND_LOOP_STAGE) {
            sawOpcodeLoop(seen);
        } else {
            sawOpcodeBug(seen);
        }
    }
1010391
@Override
    public void sawOpcode(int seen) {
        try {
            stack.precomputation(this);

            if ((seen == Const.CHECKCAST) && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item item = stack.getStackItem(0);
                if (item.getRegisterNumber() == 1) {
                    String thisCls = getClassName();
                    String equalsCls = getClassConstantOperand();
                    if (!thisCls.equals(equalsCls)) {
                        JavaClass thisJavaClass = getClassContext().getJavaClass();
                        JavaClass equalsJavaClass = Repository.lookupClass(equalsCls);
                        boolean inheritance = thisJavaClass.instanceOf(equalsJavaClass) || equalsJavaClass.instanceOf(thisJavaClass);

                        BugInstance bug = new BugInstance(this, BugType.NSE_NON_SYMMETRIC_EQUALS.name(), inheritance ? LOW_PRIORITY : NORMAL_PRIORITY)
                                .addClass(this).addMethod(this).addSourceLine(this).addString(equalsCls);
                        Map<String, BugInstance> bugs = possibleBugs.get(thisCls);
                        if (bugs == null) {
                            bugs = new HashMap<>();
                            possibleBugs.put(thisCls, bugs);
                        }
                        bugs.put(equalsCls, bug);
                    }
                }
            }
        } catch (ClassNotFoundException cnfe) {
            bugReporter.reportMissingClass(cnfe);
        } finally {
            stack.sawOpcode(this, seen);
        }
    }
191517
@Override
    public void sawOpcode(int seen) {
        // System.out.println(getPC() + " " + Const.getOpcodeName(seen) + " " + state);

        if (stack.getStackDepth() < stackDepth) {
            state = FormatState.NONE;
            stackDepth = 0;
            arguments = null;
        }
        if (seen == Const.ANEWARRAY && stack.getStackDepth() >= 2) {
            Object size = stack.getStackItem(0).getConstant();
            Object formatStr = stack.getStackItem(1).getConstant();
            if (size instanceof Integer && formatStr instanceof String) {
                arguments = new OpcodeStack.Item[(Integer) size];
                this.formatString = (String) formatStr;
                state = FormatState.READY_FOR_FORMAT;
                stackDepth = stack.getStackDepth();
            }
        } else if (state == FormatState.READY_FOR_FORMAT && seen == Const.DUP) {
            state = FormatState.EXPECTING_ASSIGNMENT;
        } else if (state == FormatState.EXPECTING_ASSIGNMENT && stack.getStackDepth() == stackDepth + 3 && seen == Const.AASTORE) {
            Object pos = stack.getStackItem(1).getConstant();
            OpcodeStack.Item value = stack.getStackItem(0);
            if (pos instanceof Integer) {
                int index = (Integer) pos;
                if (index >= 0 && index < arguments.length) {
                    arguments[index] = value;
                    state = FormatState.READY_FOR_FORMAT;
                } else {
                    state = FormatState.NONE;
                }
            } else {
                state = FormatState.NONE;
            }
        } else if (state == FormatState.READY_FOR_FORMAT
                && (seen == Const.INVOKESPECIAL || seen == Const.INVOKEVIRTUAL || seen == Const.INVOKESTATIC || seen == Const.INVOKEINTERFACE)
                && stack.getStackDepth() == stackDepth) {

            String cl = getClassConstantOperand();
            String nm = getNameConstantOperand();
            String sig = getSigConstantOperand();
            XMethod m = getXMethodOperand();
            if ((m == null || m.isVarArgs())
                    && sig.indexOf("Ljava/lang/String;[Ljava/lang/Object;)") >= 0
                    && ("java/util/Formatter".equals(cl) && "format".equals(nm) || "java/lang/String".equals(cl)
                            && "format".equals(nm) || "java/io/PrintStream".equals(cl) && "format".equals(nm)
                            || "java/io/PrintStream".equals(cl) && "printf".equals(nm) || cl.endsWith("Writer")
                            && "format".equals(nm) || cl.endsWith("Writer") && "printf".equals(nm)) || cl.endsWith("Logger")
                            && nm.endsWith("fmt")) {

                if (formatString.indexOf('\n') >= 0) {
                    bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_USES_NEWLINE", NORMAL_PRIORITY)
                    .addClassAndMethod(this).addCalledMethod(this).addString(formatString)
                    .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
                }
            }

        }
    }
1285212
@Override
    public void sawOpcode(int seen) {
        if (seen != Const.INVOKESPECIAL) {
            return;
        }

        String calledClassName = getClassConstantOperand();
        String calledMethodName = getNameConstantOperand();
        String calledMethodSig = getSigConstantOperand();
        if (calledClassName.equals(getSuperclassName()) && calledMethodName.equals(getMethodName())
                && calledMethodSig.equals(getMethodSig())) {
            sawCallToSuper = true;
        }

    }
1019802
@Override
    public void sawOpcode(int seen) {
        if (state == State.SAW_NOTHING) {
            if (seen == Const.INVOKESPECIAL) {
                state = State.SAW_INVOKESPECIAL;
            }
        } else if (state == State.SAW_INVOKESPECIAL) {
            handleInvokeSpecialState(seen);
        }
    }
706635
@Override
    public void sawOpcode(int seen) {

        if ((seen == Const.INVOKEINTERFACE) || (seen == Const.INVOKEVIRTUAL) || (seen == Const.INVOKESPECIAL) || (seen == Const.INVOKESTATIC)) {
            String clsName = getClassConstantOperand();
            String mName = getNameConstantOperand();
            String methodInfo = clsName + ':' + mName;
            String thisMethodInfo = (clsName.equals(getClassName())) ? (mName + ':' + methodSig) : "0";

            if (expensiveCalls.contains(methodInfo) || expensiveThisCalls.contains(thisMethodInfo)) {
                if (isListenerMethod) {
                    bugReporter.reportBug(new BugInstance(this, BugType.SG_SLUGGISH_GUI.name(), NORMAL_PRIORITY).addClass(this)
                            .addMethod(this.getClassContext().getJavaClass(), listenerCode.get(this.getCode())));
                } else {
                    expensiveThisCalls.add(getMethodName() + ':' + getMethodSig());
                }
                throw new StopOpcodeParsingException();
            }
        }
    }
1293491
@Override
    public void sawOpcode(int seen) {
        try {
            stack.precomputation(this);

            if (OpcodeUtils.isStore(seen) || OpcodeUtils.isLoad(seen)) {
                int reg = getRegisterOperand();
                unusedParms.clear(reg);
            } else if (OpcodeUtils.isReturn(seen) && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item item = stack.getStackItem(0);
                int reg = item.getRegisterNumber();
                if (reg >= 0) {
                    unusedParms.clear(reg);
                }
            }
            if (unusedParms.isEmpty()) {
                throw new StopOpcodeParsingException();
            }
        } finally {
            stack.sawOpcode(this, seen);
        }
    }
129885
@Override
    public void sawOpcode(int seen) {
        if ((seen == Const.GETFIELD) || (seen == Const.PUTFIELD)) {
            String fieldName = getNameConstantOperand();
            FieldInfo fi = localizableFields.get(fieldName);
            if (fi != null) {
                SourceLineAnnotation sla = SourceLineAnnotation.fromVisitedInstruction(this);
                fi.setSrcLineAnnotation(sla);
            }
        }
    }
42718
@Override
    public void sawOpcode(int seen) {
        try {
            stack.precomputation(this);

            if (seen == Const.INVOKEVIRTUAL) {
                String clsName = getClassConstantOperand();
                if ("java/io/ObjectOutputStream".equals(clsName)) {
                    String name = getNameConstantOperand();
                    if ("writeObject".equals(name) && (stack.getStackDepth() > 0)) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        JavaClass cls = item.getJavaClass();

                        if ((cls != null) && (cls.getClassName().indexOf(Values.INNER_CLASS_SEPARATOR) >= 0) && hasOuterClassSyntheticReference(cls)) {
                            bugReporter.reportBug(new BugInstance(this, BugType.PUS_POSSIBLE_UNSUSPECTED_SERIALIZATION.name(), NORMAL_PRIORITY).addClass(this)
                                    .addMethod(this).addSourceLine(this));
                        }
                    }
                }
            }
        } catch (ClassNotFoundException cnfe) {
            bugReporter.reportMissingClass(cnfe);
        } finally {
            stack.sawOpcode(this, seen);
        }
    }
629932
@Override
    public void sawOpcode(int seen) {
        try {
            stack.precomputation(this);

            if (seen == Const.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);
        }
    }
268794
@Override
	public void sawOpcode(final int seen) {
		try {
			stack.precomputation(this);

			if (seen == Const.INVOKEINTERFACE) {
				processInvokeInterface();
			} else if (seen == Const.INVOKEVIRTUAL) {
				processInvokeVirtual();
			} else if ((seen == Const.ARETURN) && (stack.getStackDepth() > 0)) {
				OpcodeStack.Item item = stack.getStackItem(0);
				XField field = item.getXField();
				if (field != null) {
					String fieldName = field.getName();
					fieldsReported.remove(fieldName);
					if (fieldsReported.isEmpty()) {
						throw new StopOpcodeParsingException();
					}
				}
			}
		} finally {
			stack.sawOpcode(this, seen);
		}
	}
735711
@Override
    public void sawOpcode(int seen) {
        if (seen == Const.INVOKEVIRTUAL) {
            if ("equals".equals(getNameConstantOperand()) && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand())) {
                OpcodeStack.Item item = stack.getStackItem(1);
                ClassDescriptor c = DescriptorFactory.createClassDescriptorFromSignature(item.getSignature());
                check(c);

            } else if ("java/lang/Class".equals(getClassConstantOperand())
                    && ("isInstance".equals(getNameConstantOperand()) || "cast".equals(getNameConstantOperand()))) {
                OpcodeStack.Item item = stack.getStackItem(1);
                if ("Ljava/lang/Class;".equals(item.getSignature())) {
                    Object value = item.getConstant();
                    if (value instanceof String) {
                        ClassDescriptor c = DescriptorFactory.createClassDescriptor((String) value);
                        check(c);
                    }
                }

            }

        } else if (seen == Const.INSTANCEOF || seen == Const.CHECKCAST) {
            check(getClassDescriptorOperand());
        }

    }
1165918
@Override
    public void sawOpcode(int seen) {
        boolean srcField = false;
        try {
            stack.precomputation(this);

            switch (seen) {
                case Const.ALOAD_0:
                    srcField = true;
                break;

                case Const.DUP:
                    if (stack.getStackDepth() > 0) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        if (item.getUserValue() != null) {
                            srcField = true;
                        }
                    }
                break;

                case Const.GETFIELD:
                    if (stack.getStackDepth() > 0) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        if (item.getRegisterNumber() == 0) {
                            srcField = true;
                        }
                    }
                break;

                case Const.PUTFIELD:
                    if (stack.getStackDepth() >= 2) {
                        OpcodeStack.Item item = stack.getStackItem(1);
                        if ((item.getRegisterNumber() == 0) || (item.getUserValue() != null)) {
                            bugReporter.reportBug(new BugInstance(this, BugType.SCA_SUSPICIOUS_CLONE_ALGORITHM.name(), NORMAL_PRIORITY).addClass(this)
                                    .addMethod(this).addSourceLine(this));
                        }
                    }

                break;

                case Const.INVOKEINTERFACE:
                case Const.INVOKEVIRTUAL:
                    String sig = getSigConstantOperand();
                    int numArgs = SignatureUtils.getNumParameters(sig);
                    if (stack.getStackDepth() > numArgs) {
                        OpcodeStack.Item item = stack.getStackItem(numArgs);
                        if ((item.getRegisterNumber() == 0) || (item.getUserValue() != null)) {
                            String name = getNameConstantOperand();
                            Integer priority = changingMethods.get(name);
                            if (priority != null) {
                                bugReporter.reportBug(new BugInstance(this, BugType.SCA_SUSPICIOUS_CLONE_ALGORITHM.name(), priority.intValue()).addClass(this)
                                        .addMethod(this).addSourceLine(this));
                            }
                        }
                    }
                break;

                default:
                break;
            }
        } finally {
            TernaryPatcher.pre(stack, seen);
            stack.sawOpcode(this, seen);
            TernaryPatcher.post(stack, seen);
            if (srcField && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item item = stack.getStackItem(0);
                item.setUserValue(Boolean.TRUE);
            }
        }
    }
1215066
@Override
    public void sawOpcode(final int seen) {

        try {
            stack.precomputation(this);

            if (OpcodeUtils.isInvoke(seen)) {
                String methodSig = getSigConstantOperand();
                List<String> parmTypes = SignatureUtils.getParameterSignatures(methodSig);
                int stackDepth = stack.getStackDepth();
                if (stackDepth >= parmTypes.size()) {
                    for (int i = 0; i < parmTypes.size(); i++) {
                        OpcodeStack.Item itm = stack.getStackItem(i);
                        int reg = itm.getRegisterNumber();
                        removeUselessDefiners(parmTypes.get(parmTypes.size() - i - 1), reg);
                    }
                }

                if ((seen != Const.INVOKESPECIAL) && (seen != Const.INVOKESTATIC)) {
                    if (stackDepth > parmTypes.size()) {
                        OpcodeStack.Item itm = stack.getStackItem(parmTypes.size());
                        int reg = itm.getRegisterNumber();
                        int parm = reg;
                        if (!methodIsStatic) {
                            parm--;
                        }
                        if ((parm >= 0) && (parm < parmCount)) {
                            removeUselessDefiners(reg);
                        }
                    } else {
                        parameterDefiners.clear();
                    }
                }
            } else if ((seen == Const.PUTFIELD) || (seen == Const.GETFIELD) || (seen == Const.PUTSTATIC)
                    || (seen == Const.GETSTATIC) || OpcodeUtils.isAStore(seen)) {
                // Don't check parameters that are aliased
                if (stack.getStackDepth() > 0) {
                    OpcodeStack.Item itm = stack.getStackItem(0);
                    int reg = itm.getRegisterNumber();
                    int parm = reg;
                    if (!methodIsStatic) {
                        parm--;
                    }
                    if ((parm >= 0) && (parm < parmCount)) {
                        parameterDefiners.remove(Integer.valueOf(reg));
                    }
                } else {
                    parameterDefiners.clear();
                }

                if ((seen == Const.GETFIELD) || (seen == Const.PUTFIELD)) {
                    if (stack.getStackDepth() > 1) {
                        OpcodeStack.Item itm = stack.getStackItem(1);
                        int reg = itm.getRegisterNumber();
                        int parm = reg;
                        if (!methodIsStatic) {
                            parm--;
                        }
                        if ((parm >= 0) && (parm < parmCount)) {
                            parameterDefiners.remove(Integer.valueOf(reg));
                        }
                    } else {
                        parameterDefiners.clear();
                    }
                }

            } else if (OpcodeUtils.isALoad(seen)) {
                int reg = RegisterUtils.getALoadReg(this, seen);

                int parm = reg;
                if (!methodIsStatic) {
                    parm--;
                }
                if ((parm >= 0) && (parm < parmCount)) {
                    usedParameters.set(reg);
                }
            } else if (seen == Const.AASTORE) {
                // Don't check parameters that are stored in
                if (stack.getStackDepth() >= 3) {
                    OpcodeStack.Item itm = stack.getStackItem(0);
                    int reg = itm.getRegisterNumber();
                    int parm = reg;
                    if (!methodIsStatic) {
                        parm--;
                    }
                    if ((parm >= 0) && (parm < parmCount)) {
                        parameterDefiners.remove(Integer.valueOf(reg));
                    }
                } else {
                    parameterDefiners.clear();
                }
            } else if (seen == Const.ARETURN) {
                if (stack.getStackDepth() >= 1) {
                    OpcodeStack.Item item = stack.getStackItem(0);
                    int reg = item.getRegisterNumber();
                    int parm = reg;
                    if (!methodIsStatic) {
                        parm--;
                    }

                    if ((parm >= 0) && (parm < parmCount)) {
                        parameterDefiners.remove(Integer.valueOf(reg));
                    }
                } else {
                    parameterDefiners.clear();
                }
            }

            if (parameterDefiners.isEmpty()) {
                throw new StopOpcodeParsingException();
            }
        } finally {
            stack.sawOpcode(this, seen);
        }
    }
22794
@Override
    public void sawOpcode(int seen) {
        boolean sawSBToString = false;
        try {

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

                    Integer register = Integer.valueOf(item.getRegisterNumber());
                    Object constant = registerConstants.get(register);
                    if (CONSTANT_DOESNT_EXIST.equals(constant)) {
                        throw new StopOpcodeParsingException();
                    }

                    String returnSig = item.getSignature();
                    if ((returnSig != null) && returnSig.startsWith(Values.SIG_ARRAY_PREFIX)) {
                        XField f = item.getXField();
                        if ((f == null) || (!f.isStatic())) {
                            throw new StopOpcodeParsingException();
                        }
                    }

                    constant = item.getConstant();
                    if (constant == null) {
                        throw new StopOpcodeParsingException();
                    }
                    if (Boolean.TRUE.equals(item.getUserValue()) && ("".equals(constant))) {
                        throw new StopOpcodeParsingException();
                    }
                    if ((returnConstant != null) && (!returnConstant.equals(constant))) {
                        throw new StopOpcodeParsingException();
                    }

                    returnRegister = Integer.valueOf(item.getRegisterNumber());
                    returnConstant = constant;
                    returnPC = getPC();
                }
            } else if ((seen == Const.GOTO) || (seen == Const.GOTO_W)) {
                if (stack.getStackDepth() > 0) {
                    // Ternaries confuse us too much, if the code has a ternary well - oh well
                    throw new StopOpcodeParsingException();
                }
            } else if (seen == Const.ATHROW) {
                throw new StopOpcodeParsingException();
            } else if (seen == Const.INVOKEVIRTUAL) {
                String clsName = getClassConstantOperand();
                if (SignatureUtils.isPlainStringConvertableClass(clsName)) {
                    sawSBToString = Values.TOSTRING.equals(getNameConstantOperand());
                }
            } else if (((seen >= Const.ISTORE) && (seen <= Const.ASTORE_3)) || (seen == Const.IINC)) {
                Integer register = Integer.valueOf(getRegisterOperand());
                if ((returnRegister.intValue() != -1) && (register.equals(returnRegister))) {
                    throw new StopOpcodeParsingException();
                }

                if (stack.getStackDepth() > 0) {
                    OpcodeStack.Item item = stack.getStackItem(0);
                    Object constant = item.getConstant();

                    Object regConstant = registerConstants.get(register);
                    if (regConstant != null) {
                        if ((constant == null) || !constant.equals(regConstant)) {
                            registerConstants.put(register, CONSTANT_DOESNT_EXIST);
                        }
                    } else {
                        if (item.getSignature().contains(Values.SIG_ARRAY_PREFIX)) {
                            registerConstants.put(register, CONSTANT_DOESNT_EXIST);
                        } else {
                            registerConstants.put(register, constant == null ? CONSTANT_DOESNT_EXIST : constant);
                        }
                    }
                } else {
                    registerConstants.put(register, CONSTANT_DOESNT_EXIST);
                }

                if (returnRegister.equals(register)) {
                    Object constant = registerConstants.get(returnRegister);
                    if (CONSTANT_DOESNT_EXIST.equals(constant)) {
                        throw new StopOpcodeParsingException();
                    }
                }
            }

        } 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);
            }
        }
    }
1071352
@Override
    public void sawOpcode(int seen) {
        String methodInfo = null;
        try {
            stack.precomputation(this);

            switch (seen) {
                case Const.INVOKEVIRTUAL:
                case Const.INVOKEINTERFACE:
                case Const.INVOKESTATIC:
                    String sig = getSigConstantOperand();
                    if (!sig.endsWith(Values.SIG_VOID)) {
                        methodInfo = getClassConstantOperand() + '@' + getNameConstantOperand() + getSigConstantOperand();
                    }
                break;

                case Const.POP:
                case Const.POP2:
                    if (stack.getStackDepth() > 0) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        String mInfo = (String) item.getUserValue();
                        if (mInfo != null) {
                            for (Pattern p : IMMUTABLE_METHODS) {
                                Matcher m = p.matcher(mInfo);

                                if (m.matches()) {
                                    bugReporter.reportBug(new BugInstance(this, BugType.NPMC_NON_PRODUCTIVE_METHOD_CALL.name(), NORMAL_PRIORITY).addClass(this)
                                            .addMethod(this).addSourceLine(this).addString(mInfo));
                                    break;
                                }
                            }
                        }
                    }
                break;
            }

        } finally {
            stack.sawOpcode(this, seen);
            if ((methodInfo != null) && (stack.getStackDepth() > 0)) {
                OpcodeStack.Item item = stack.getStackItem(0);
                item.setUserValue(methodInfo);
            }
        }
    }