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 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); } } } }
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); } }
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(); }
/** * 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); } } }
/** * 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); } } } }
/** * 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); } } } }
@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); } } } }