/* * FindBugs - Find bugs in Java programs * Copyright (C) 2005, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.detect; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import org.apache.bcel.Repository; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.CodeException; import org.apache.bcel.classfile.JavaClass; import edu.umd.cs.findbugs.BugAccumulator; import edu.umd.cs.findbugs.BugAnnotation; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.BugReporter; import edu.umd.cs.findbugs.FieldAnnotation; import edu.umd.cs.findbugs.IntAnnotation; import edu.umd.cs.findbugs.LocalVariableAnnotation; import edu.umd.cs.findbugs.MethodAnnotation; import edu.umd.cs.findbugs.OpcodeStack; import edu.umd.cs.findbugs.OpcodeStack.Item; import edu.umd.cs.findbugs.Priorities; import edu.umd.cs.findbugs.SourceLineAnnotation; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.XFactory; import edu.umd.cs.findbugs.ba.XField; import edu.umd.cs.findbugs.ba.XMethod; import edu.umd.cs.findbugs.ba.ch.Subtypes2; import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; import edu.umd.cs.findbugs.classfile.ClassDescriptor; import edu.umd.cs.findbugs.classfile.DescriptorFactory; import edu.umd.cs.findbugs.classfile.FieldDescriptor; import edu.umd.cs.findbugs.visitclass.Util; public class FindPuzzlers extends OpcodeStackDetector { static FieldDescriptor SYSTEM_OUT = new FieldDescriptor("java/lang/System", "out", "Ljava/io/PrintStream;", true); static FieldDescriptor SYSTEM_ERR = new FieldDescriptor("java/lang/System", "err", "Ljava/io/PrintStream;", true); final BugReporter bugReporter; final BugAccumulator bugAccumulator; public FindPuzzlers(BugReporter bugReporter) { this.bugReporter = bugReporter; this.bugAccumulator = new BugAccumulator(bugReporter); } @Override public void visit(Code obj) { prevOpcodeIncrementedRegister = -1; best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG = LOW_PRIORITY + 1; prevOpCode = NOP; previousMethodInvocation = null; badlyComputingOddState = 0; resetIMulCastLong(); imul_distance = 10000; ternaryConversionState = 0; becameTop = -1; super.visit(obj); bugAccumulator.reportAccumulatedBugs(); pendingUnreachableBranch = null; } int becameTop; int imul_constant; int imul_distance; boolean imul_operand_is_parameter; int prevOpcodeIncrementedRegister; int valueOfConstantArgumentToShift; int best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG; boolean constantArgumentToShift; boolean shiftOfNonnegativeValue; int ternaryConversionState = 0; int badlyComputingOddState; int prevOpCode; XMethod previousMethodInvocation; boolean isTigerOrHigher; static ClassDescriptor ITERATOR = DescriptorFactory.createClassDescriptor(Iterator.class); static ClassDescriptor MAP_ENTRY = DescriptorFactory.createClassDescriptor(Map.Entry.class); @Override public void visit(JavaClass obj) { isTigerOrHigher = obj.getMajor() >= MAJOR_1_5; try { Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2(); ClassDescriptor me = getClassDescriptor(); if (subtypes2.isSubtype(me, MAP_ENTRY) && subtypes2.isSubtype(me, ITERATOR)) { bugReporter.reportBug(new BugInstance(this, "PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS", NORMAL_PRIORITY) .addClass(this).addString("shouldn't reuse Iterator as a Map.Entry")); } } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); } } private void resetIMulCastLong() { imul_constant = 1; imul_operand_is_parameter = false; } private int adjustPriority(int factor, int priority) { if (factor <= 4) return LOW_PRIORITY + 2; if (factor <= 10000) return priority + 1; if (factor <= 60 * 60 * 1000) return priority; return priority - 1; } private int adjustMultiplier(Object constant, int mul) { if (!(constant instanceof Integer)) return mul; return Math.abs(((Integer) constant).intValue()) * mul; } @Override public boolean beforeOpcode(int seen) { super.beforeOpcode(seen); return true; } BugInstance pendingUnreachableBranch; @Override public void sawOpcode(int seen) { if (stack.isTop()) { pendingUnreachableBranch = null; if (becameTop == -1) becameTop = getPC(); if (seen == GOTO && getBranchTarget() < becameTop) { pendingUnreachableBranch = new BugInstance(this, "TESTING", NORMAL_PRIORITY) .addClassAndMethod(this).addString("Unreachable loop body").addSourceLineRange(this, becameTop, getPC()); } return; } if (pendingUnreachableBranch != null) { bugReporter.reportBug(pendingUnreachableBranch); pendingUnreachableBranch = null; } becameTop = -1; if (seen == INVOKESPECIAL && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("(Ljava/util/Collection;)V") && getClassConstantOperand().contains("Set") || (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && getNameConstantOperand().equals("addAll") && getSigConstantOperand().equals("(Ljava/util/Collection;)Z")) { OpcodeStack.Item top = stack.getStackItem(0); XMethod returnValueOf = top.getReturnValueOf(); if (returnValueOf != null && returnValueOf.getName().equals("entrySet")) { String name = returnValueOf.getClassName(); int priority = Priorities.LOW_PRIORITY; if (name.equals("java.util.Map")) priority = Priorities.NORMAL_PRIORITY; else if (name.equals(EnumMap.class.getName()) || name.equals(IdentityHashMap.class.getName())) priority = Priorities.HIGH_PRIORITY; bugReporter.reportBug(new BugInstance(this, "DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS", priority) .addClassAndMethod(this).addCalledMethod(returnValueOf).addCalledMethod(this).addValueSource(top, this).addSourceLine(this)); } } if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("hashCode") && getSigConstantOperand().equals("()I") && stack.getStackDepth() > 0) { OpcodeStack.Item item0 = stack.getStackItem(0); if (item0.getSignature().charAt(0) == '[') bugReporter.reportBug(new BugInstance(this, "DMI_INVOKING_HASHCODE_ON_ARRAY", NORMAL_PRIORITY) .addClassAndMethod(this).addValueSource(item0, this).addSourceLine(this)); } if (seen != RETURN && isReturn(seen) && isRegisterStore(getPrevOpcode(1))) { int priority = Priorities.NORMAL_PRIORITY; if (getMethodSig().endsWith(")Z")) priority = Priorities.HIGH_PRIORITY; else { if (getMethodSig().endsWith(")Ljava/lang/String;")) priority = Priorities.LOW_PRIORITY; if (getPC() == getCode().getCode().length - 1) priority++; } bugReporter.reportBug(new BugInstance(this, "DLS_DEAD_LOCAL_STORE_IN_RETURN", priority).addClassAndMethod(this) .addSourceLine(this)); } // System.out.println(getPC() + " " + OPCODE_NAMES[seen] + " " + // ternaryConversionState); if (seen == IMUL) { if (imul_distance != 1) resetIMulCastLong(); imul_distance = 0; if (stack.getStackDepth() > 1) { OpcodeStack.Item item0 = stack.getStackItem(0); OpcodeStack.Item item1 = stack.getStackItem(1); imul_constant = adjustMultiplier(item0.getConstant(), imul_constant); imul_constant = adjustMultiplier(item1.getConstant(), imul_constant); if (item0.isInitialParameter() || item1.isInitialParameter()) imul_operand_is_parameter = true; } } else { imul_distance++; } if (prevOpCode == IMUL && seen == I2L) { int priority = adjustPriority(imul_constant, NORMAL_PRIORITY); if (priority >= LOW_PRIORITY && imul_constant != 1000 && imul_constant != 60 && imul_operand_is_parameter) priority = NORMAL_PRIORITY; if (priority <= best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG) { best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG = priority; bugAccumulator.accumulateBug( new BugInstance(this, "ICAST_INTEGER_MULTIPLY_CAST_TO_LONG", priority).addClassAndMethod(this), this); } } if (getMethodName().equals("<clinit>") && (seen == PUTSTATIC || seen == GETSTATIC || seen == INVOKESTATIC)) { String clazz = getClassConstantOperand(); if (!clazz.equals(getClassName())) { try { JavaClass targetClass = Repository.lookupClass(clazz); if (Repository.instanceOf(targetClass, getThisClass())) { int priority = NORMAL_PRIORITY; if (seen == GETSTATIC) priority--; if (!targetClass.isPublic()) priority++; bugAccumulator.accumulateBug(new BugInstance(this, "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", priority).addClassAndMethod(this).addClass(getClassConstantOperand()), this); } } catch (ClassNotFoundException e) { // ignore it } } } if (false && (seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("equals") && getSigConstantOperand().equals("(Ljava/lang/Object;)Z") && stack.getStackDepth() > 1) { OpcodeStack.Item item0 = stack.getStackItem(0); OpcodeStack.Item item1 = stack.getStackItem(1); if (item0.isArray() || item1.isArray()) { bugAccumulator.accumulateBug( new BugInstance(this, "EC_BAD_ARRAY_COMPARE", NORMAL_PRIORITY).addClassAndMethod(this), this); } } if (seen >= IALOAD && seen <= SALOAD || seen >= IASTORE && seen <= SASTORE) { Item index = stack.getStackItem(0); if (index.getSpecialKind() == Item.AVERAGE_COMPUTED_USING_DIVISION) { SourceLineAnnotation where; if (index.getPC() >= 0) where = SourceLineAnnotation.fromVisitedInstruction(this, index.getPC()); else where = SourceLineAnnotation.fromVisitedInstruction(this); bugAccumulator.accumulateBug( new BugInstance(this, "IM_AVERAGE_COMPUTATION_COULD_OVERFLOW", NORMAL_PRIORITY).addClassAndMethod(this), where); } } if ((seen == IFEQ || seen == IFNE) && getPrevOpcode(1) == IMUL && (getPrevOpcode(2) == SIPUSH || getPrevOpcode(2) == BIPUSH) && getPrevOpcode(3) == IREM) bugAccumulator.accumulateBug( new BugInstance(this, "IM_MULTIPLYING_RESULT_OF_IREM", LOW_PRIORITY).addClassAndMethod(this), this); if (seen == I2S && getPrevOpcode(1) == IUSHR && !shiftOfNonnegativeValue && (!constantArgumentToShift || valueOfConstantArgumentToShift % 16 != 0) || seen == I2B && getPrevOpcode(1) == IUSHR && !shiftOfNonnegativeValue && (!constantArgumentToShift || valueOfConstantArgumentToShift % 8 != 0)) bugAccumulator.accumulateBug( new BugInstance(this, "ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT", NORMAL_PRIORITY).addClassAndMethod(this), this); constantArgumentToShift = false; shiftOfNonnegativeValue = false; if ((seen == IUSHR || seen == ISHR || seen == ISHL)) { if (stack.getStackDepth() <= 1) { // don't understand; lie so other detectors won't get concerned constantArgumentToShift = true; valueOfConstantArgumentToShift = 8; } else { Object rightHandSide = stack.getStackItem(0).getConstant(); Object leftHandSide = stack.getStackItem(1).getConstant(); shiftOfNonnegativeValue = stack.getStackItem(1).isNonNegative(); if (rightHandSide instanceof Integer) { constantArgumentToShift = true; valueOfConstantArgumentToShift = ((Integer) rightHandSide); if (valueOfConstantArgumentToShift < 0 || valueOfConstantArgumentToShift >= 32) bugAccumulator.accumulateBug(new BugInstance(this, "ICAST_BAD_SHIFT_AMOUNT", valueOfConstantArgumentToShift < 0 ? LOW_PRIORITY : (valueOfConstantArgumentToShift == 32 && getMethodName().equals("hashCode") ? NORMAL_PRIORITY : HIGH_PRIORITY)) .addClassAndMethod(this).addInt(valueOfConstantArgumentToShift).describe(IntAnnotation.INT_SHIFT) .addValueSource(stack.getStackItem(1), this), this); } if (leftHandSide != null && leftHandSide instanceof Integer && ((Integer) leftHandSide) > 0) { // boring; lie so other detectors won't get concerned constantArgumentToShift = true; valueOfConstantArgumentToShift = 8; } } } if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 0 && (getClassConstantOperand().equals("java/util/Date") || getClassConstantOperand().equals("java/sql/Date")) && getNameConstantOperand().equals("setMonth") && getSigConstantOperand().equals("(I)V")) { OpcodeStack.Item item = stack.getStackItem(0); Object o = item.getConstant(); if (o != null && o instanceof Integer) { int v = (Integer) o; if (v < 0 || v > 11) bugReporter.reportBug(new BugInstance(this, "DMI_BAD_MONTH", HIGH_PRIORITY).addClassAndMethod(this).addInt(v) .describe(IntAnnotation.INT_VALUE).addCalledMethod(this).addSourceLine(this)); } } if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 1 && getClassConstantOperand().equals("java/util/Calendar") && getNameConstantOperand().equals("set") || seen == INVOKESPECIAL && stack.getStackDepth() > 1 && getClassConstantOperand().equals("java/util/GregorianCalendar") && getNameConstantOperand().equals("<init>") ) { String sig = getSigConstantOperand(); if (sig.startsWith("(III")) { int pos = sig.length() - 5; OpcodeStack.Item item = stack.getStackItem(pos); Object o = item.getConstant(); if (o != null && o instanceof Integer) { int v = (Integer) o; if (v < 0 || v > 11) bugReporter.reportBug(new BugInstance(this, "DMI_BAD_MONTH", NORMAL_PRIORITY).addClassAndMethod(this) .addInt(v).describe(IntAnnotation.INT_VALUE).addCalledMethod(this).addSourceLine(this)); } } } if (isRegisterStore() && (seen == ISTORE || seen == ISTORE_0 || seen == ISTORE_1 || seen == ISTORE_2 || seen == ISTORE_3) && getRegisterOperand() == prevOpcodeIncrementedRegister) { bugAccumulator.accumulateBug( new BugInstance(this, "DLS_OVERWRITTEN_INCREMENT", HIGH_PRIORITY).addClassAndMethod(this), this); } if (seen == IINC) { prevOpcodeIncrementedRegister = getRegisterOperand(); } else prevOpcodeIncrementedRegister = -1; // Java Puzzlers, Chapter 2, puzzle 1 // Look for ICONST_2 IREM ICONST_1 IF_ICMPNE L1 switch (badlyComputingOddState) { case 0: if (seen == ICONST_2) badlyComputingOddState++; break; case 1: if (seen == IREM) { OpcodeStack.Item item = stack.getStackItem(1); if (!item.isNonNegative() && item.getSpecialKind() != OpcodeStack.Item.MATH_ABS) badlyComputingOddState++; else badlyComputingOddState = 0; } else badlyComputingOddState = 0; break; case 2: if (seen == ICONST_1) badlyComputingOddState++; else badlyComputingOddState = 0; break; case 3: if (seen == IF_ICMPEQ || seen == IF_ICMPNE) { bugAccumulator.accumulateBug( new BugInstance(this, "IM_BAD_CHECK_FOR_ODD", NORMAL_PRIORITY).addClassAndMethod(this), this); } badlyComputingOddState = 0; break; } // Java Puzzlers, chapter 3, puzzle 12 if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 0 && (getNameConstantOperand().equals("toString") && getSigConstantOperand().equals("()Ljava/lang/String;") || getNameConstantOperand().equals("append") && getSigConstantOperand().equals("(Ljava/lang/Object;)Ljava/lang/StringBuilder;") && getClassConstantOperand().equals("java/lang/StringBuilder") || getNameConstantOperand().equals("append") && getSigConstantOperand().equals("(Ljava/lang/Object;)Ljava/lang/StringBuffer;") && getClassConstantOperand().equals("java/lang/StringBuffer") || (getNameConstantOperand() .equals("print") || getNameConstantOperand().equals("println")) && getSigConstantOperand().equals("(Ljava/lang/Object;)V"))) { OpcodeStack.Item item = stack.getStackItem(0); String signature = item.getSignature(); if (signature != null && signature.startsWith("[")) { boolean debuggingContext = signature.equals("[Ljava/lang/StackTraceElement;"); if (!debuggingContext) { for (CodeException e : getCode().getExceptionTable()) { if (e.getHandlerPC() <= getPC() && e.getHandlerPC() + 30 >= getPC()) debuggingContext = true; } for (int i = 1; !debuggingContext && i < stack.getStackDepth(); i++) { OpcodeStack.Item e = stack.getStackItem(i); if (e.getSignature().indexOf("Logger") >= 0 || e.getSignature().indexOf("Exception") >= 0) debuggingContext = true; XField f = e.getXField(); if (f != null && (SYSTEM_ERR.equals(f.getFieldDescriptor()) || SYSTEM_OUT.equals(f.getFieldDescriptor()))) debuggingContext = true; } } String name = null; int reg = item.getRegisterNumber(); Collection<BugAnnotation> as = new ArrayList<BugAnnotation>(); XField field = item.getXField(); FieldAnnotation fieldAnnotation = null; if (field != null) { fieldAnnotation = FieldAnnotation.fromXField(field); fieldAnnotation.setDescription(FieldAnnotation.LOADED_FROM_ROLE); } if (reg != -1) { LocalVariableAnnotation lva = LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg, getPC(), getPC() - 1); if (lva.isNamed()) { as.add(lva); if (fieldAnnotation != null) as.add(fieldAnnotation); } else { if (fieldAnnotation != null) as.add(fieldAnnotation); as.add(lva); } } else if (fieldAnnotation != null) as.add(fieldAnnotation); else { XMethod m = item.getReturnValueOf(); if (m != null) { MethodAnnotation methodAnnotation = MethodAnnotation.fromXMethod(m); methodAnnotation.setDescription(MethodAnnotation.METHOD_RETURN_VALUE_OF); as.add(methodAnnotation); } } int priority = debuggingContext ? NORMAL_PRIORITY : HIGH_PRIORITY; if (!as.isEmpty()) { bugAccumulator.accumulateBug(new BugInstance(this, "DMI_INVOKING_TOSTRING_ON_ARRAY", priority) .addClassAndMethod(this).addAnnotations(as), this); } else { bugAccumulator.accumulateBug( new BugInstance(this, "DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY", priority).addClassAndMethod(this), this); } } } if (isTigerOrHigher) { if (previousMethodInvocation != null && prevOpCode == INVOKEVIRTUAL && seen == INVOKESTATIC) { String classNameForPreviousMethod = previousMethodInvocation.getClassName(); String classNameForThisMethod = getClassConstantOperand(); if (classNameForPreviousMethod.startsWith("java.lang.") && classNameForPreviousMethod.equals(classNameForThisMethod.replace('/', '.')) && previousMethodInvocation.getName().endsWith("Value") && previousMethodInvocation.getSignature().length() == 3 && getNameConstantOperand().equals("valueOf") && getSigConstantOperand().charAt(1) == previousMethodInvocation.getSignature().charAt(2)) bugAccumulator.accumulateBug( new BugInstance(this, "BX_UNBOXING_IMMEDIATELY_REBOXED", NORMAL_PRIORITY).addClassAndMethod(this) .addCalledMethod(this), this); } if (previousMethodInvocation != null && prevOpCode == INVOKESPECIAL && seen == INVOKEVIRTUAL) { String classNameForPreviousMethod = previousMethodInvocation.getClassName(); String classNameForThisMethod = getClassConstantOperand(); if (classNameForPreviousMethod.startsWith("java.lang.") && classNameForPreviousMethod.equals(classNameForThisMethod.replace('/', '.')) && getNameConstantOperand().endsWith("Value") && getSigConstantOperand().length() == 3) { if (getSigConstantOperand().charAt(2) == previousMethodInvocation.getSignature().charAt(1)) bugAccumulator.accumulateBug( new BugInstance(this, "BX_BOXING_IMMEDIATELY_UNBOXED", NORMAL_PRIORITY).addClassAndMethod(this), this); else bugAccumulator.accumulateBug(new BugInstance(this, "BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION", NORMAL_PRIORITY).addClassAndMethod(this), this); ternaryConversionState = 1; } else ternaryConversionState = 0; } else if (seen == INVOKEVIRTUAL) { if (getClassConstantOperand().startsWith("java/lang") && getNameConstantOperand().endsWith("Value") && getSigConstantOperand().length() == 3) ternaryConversionState = 1; else ternaryConversionState = 0; } else if (ternaryConversionState == 1) { if (I2L < seen && seen <= I2S) ternaryConversionState = 2; else ternaryConversionState = 0; } else if (ternaryConversionState == 2) { ternaryConversionState = 0; if (seen == GOTO) bugReporter.reportBug(new BugInstance(this, "BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR", NORMAL_PRIORITY) .addClassAndMethod(this).addSourceLine(this)); } else ternaryConversionState = 0; } AssertInvokedFromRun: if (seen == INVOKESTATIC) if ((getNameConstantOperand().startsWith("assert") || getNameConstantOperand().startsWith("fail")) && getMethodName().equals("run") && implementsRunnable(getThisClass())) { int size1 = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getMethod().getCode(), "java/lang/Throwable", getPC()); int size2 = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getMethod().getCode(), "java/lang/Error", getPC()); int size3 = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getMethod().getCode(), "java/lang/AssertionFailureError", getPC()); int size = Math.min(Math.min(size1, size2), size3); if (size == Integer.MAX_VALUE) { String dottedClassName = getClassConstantOperand().replace('/', '.'); if (!dottedClassName.startsWith("junit")) { try { JavaClass targetClass = AnalysisContext.currentAnalysisContext().lookupClass(dottedClassName); if (!targetClass.getSuperclassName().startsWith("junit")) break AssertInvokedFromRun; } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); break AssertInvokedFromRun; } } bugAccumulator.accumulateBug(new BugInstance(this, "IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD", extendsThread(getThisClass()) ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this), this); } } if (seen == INVOKESPECIAL && getClassConstantOperand().startsWith("java/lang/") && getNameConstantOperand().equals("<init>") && getSigConstantOperand().length() == 4) previousMethodInvocation = XFactory.createReferencedXMethod(this); else if (seen == INVOKESTATIC && getClassConstantOperand().startsWith("java/lang/") && getNameConstantOperand().equals("valueOf") && getSigConstantOperand().length() == 4) previousMethodInvocation = XFactory.createReferencedXMethod(this); else if (seen == INVOKEVIRTUAL && getClassConstantOperand().startsWith("java/lang/") && getNameConstantOperand().endsWith("Value") && getSigConstantOperand().length() == 3) previousMethodInvocation = XFactory.createReferencedXMethod(this); else previousMethodInvocation = null; if (seen == IAND || seen == LAND) { OpcodeStack.Item rhs = stack.getStackItem(0); OpcodeStack.Item lhs = stack.getStackItem(1); Object constant = rhs.getConstant(); OpcodeStack.Item value = lhs; if (constant == null) { constant = lhs.getConstant(); value = rhs; } if (constant instanceof Number && (seen == LAND || value.getSpecialKind() == OpcodeStack.Item.RESULT_OF_L2I)) { long constantValue = ((Number) constant).longValue(); if ((constantValue == 0xEFFFFFFFL || constantValue == 0xEFFFFFFFFFFFFFFFL || seen == IAND && constantValue == 0xEFFFFFFF)) bugAccumulator.accumulateBug(new BugInstance(this, "TESTING", seen == LAND ? HIGH_PRIORITY : NORMAL_PRIORITY) .addClassAndMethod(this).addString("Possible failed attempt to mask lower 31 bits of an int") .addValueSource(value, this), this); } } if (seen == INEG) { OpcodeStack.Item top = stack.getStackItem(0); XMethod m = top.getReturnValueOf(); if (m != null) { if (m.getName().equals("compareTo") || m.getName().equals("compare")) bugAccumulator.accumulateBug(new BugInstance(this, "RV_NEGATING_RESULT_OF_COMPARETO", NORMAL_PRIORITY) .addClassAndMethod(this) .addCalledMethod(m).addValueSource(top, this), this); } } if (seen == IRETURN && (getMethod().getName().equals("compareTo") || getMethod().getName().equals("compare"))) { OpcodeStack.Item top = stack.getStackItem(0); Object o = top.getConstant(); if (o instanceof Integer && ((Integer)o).intValue() == Integer.MIN_VALUE) bugAccumulator.accumulateBug(new BugInstance(this, "CO_COMPARETO_RESULTS_MIN_VALUE", NORMAL_PRIORITY) .addClassAndMethod(this), this); } prevOpCode = seen; } boolean implementsRunnable(JavaClass obj) { return Subtypes2.instanceOf(obj, "java.lang.Runnable"); } boolean extendsThread(JavaClass obj) { return Subtypes2.instanceOf(obj, "java.lang.Thread"); } }