@Override public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, LockSet fact) throws DataflowAnalysisException { Instruction ins = handle.getInstruction(); short opcode = ins.getOpcode(); if (opcode == Constants.MONITORENTER || opcode == Constants.MONITOREXIT) { ValueNumberFrame frame = vnaDataflow.getFactAtLocation(new Location(handle, basicBlock)); modifyLock(frame, fact, opcode == Constants.MONITORENTER ? 1 : -1); } else if (opcode == Constants.INVOKEVIRTUAL || opcode == Constants.INVOKEINTERFACE) { InvokeInstruction inv = (InvokeInstruction) ins; String name = inv.getMethodName(methodGen.getConstantPool()); String sig = inv.getSignature(methodGen.getConstantPool()); ValueNumberFrame frame = vnaDataflow.getFactAtLocation(new Location(handle, basicBlock)); if ("()V".equals(sig) && ("lock".equals(name) || "lockInterruptibly".equals(name))) { modifyLock(frame, fact, 1); } else if ("()V".equals(sig) && ("unlock".equals(name))) { modifyLock(frame, fact, -1); } } else if ((ins instanceof ReturnInstruction) && isSynchronized && !isStatic) { lockOp(fact, vna.getThisValue().getNumber(), -1); } }
/** Checks the specific method for consistency. */ public static void checkMgen(MethodGen mgen) { if (skip_checks) return; try { mgen.toString(); mgen.getLineNumberTable(mgen.getConstantPool()); InstructionList ilist = mgen.getInstructionList(); if (ilist == null || ilist.getStart() == null) return; CodeExceptionGen[] exceptionHandlers = mgen.getExceptionHandlers(); for (CodeExceptionGen gen : exceptionHandlers) { assert ilist.contains(gen.getStartPC()) : "exception handler " + gen + " has been forgotten in " + mgen.getClassName() + "." + mgen.getName(); } MethodGen nmg = new MethodGen(mgen.getMethod(), mgen.getClassName(), mgen.getConstantPool()); nmg.getLineNumberTable(mgen.getConstantPool()); } catch (Throwable t) { System.out.printf("failure in method %s.%s\n", mgen.getClassName(), mgen.getName()); t.printStackTrace(); throw new Error(t); } }
/** * If this is a putfield or putstatic instruction, check to see if the field is @NonNull, and * treat it as dereferences. * * @param location the Location of the instruction * @param vnaFrame the ValueNumberFrame at the Location of the instruction * @param fact the dataflow value to modify * @throws DataflowAnalysisException */ private void checkNonNullPutField( Location location, ValueNumberFrame vnaFrame, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { INullnessAnnotationDatabase database = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase(); FieldInstruction fieldIns = (FieldInstruction) location.getHandle().getInstruction(); XField field = XFactory.createXField(fieldIns, methodGen.getConstantPool()); char firstChar = field.getSignature().charAt(0); if (firstChar != 'L' && firstChar != '[') { return; } NullnessAnnotation resolvedAnnotation = database.getResolvedAnnotation(field, true); if (resolvedAnnotation == NullnessAnnotation.NONNULL) { IsNullValueFrame invFrame = invDataflow.getFactAtLocation(location); if (!invFrame.isValid()) { return; } IsNullValue value = invFrame.getTopValue(); if (reportDereference(value)) { ValueNumber vn = vnaFrame.getTopValue(); fact.addDeref(vn, location); } } }
/** * Remove the local variable type table attribute (LVTT) from mg. Evidently some changes require * this to be updated, but without BCEL support that would be hard to do. It should be safe to * just delete it since it is optional and really only of use to a debugger. */ public static void remove_local_variable_type_tables(MethodGen mg) { for (Attribute a : mg.getCodeAttributes()) { if (is_local_variable_type_table(a, mg.getConstantPool())) { mg.removeCodeAttribute(a); } } }
/** * If this is a method call instruction, check to see if any of the parameters are @NonNull, and * treat them as dereferences. * * @param location the Location of the instruction * @param vnaFrame the ValueNumberFrame at the Location of the instruction * @param fact the dataflow value to modify * @throws DataflowAnalysisException */ private void checkNonNullParams( Location location, ValueNumberFrame vnaFrame, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { ConstantPoolGen constantPool = methodGen.getConstantPool(); Set<ValueNumber> nonNullParams = checkNonNullParams( location, vnaFrame, constantPool, method, invDataflow.getFactAtLocation(location)); for (ValueNumber vn : nonNullParams) { fact.addDeref(vn, location); } }
/** * Check method call at given location to see if it unconditionally dereferences a parameter. Mark * any such arguments as derefs. * * @param location the Location of the method call * @param vnaFrame ValueNumberFrame at the Location * @param fact the dataflow value to modify * @throws DataflowAnalysisException */ private void checkUnconditionalDerefDatabase( Location location, ValueNumberFrame vnaFrame, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { ConstantPoolGen constantPool = methodGen.getConstantPool(); for (ValueNumber vn : checkUnconditionalDerefDatabase( location, vnaFrame, constantPool, invDataflow.getFactAtLocation(location), typeDataflow)) { fact.addDeref(vn, location); } }
/** * Determine whether dataflow should be propagated on given edge. * * @param edge the edge * @return true if dataflow should be propagated on the edge, false otherwise */ private boolean isExceptionEdge(Edge edge) { boolean isExceptionEdge = edge.isExceptionEdge(); if (isExceptionEdge) { if (DEBUG) { System.out.println("NOT Ignoring " + edge); } return true; // false } if (edge.getType() != EdgeTypes.FALL_THROUGH_EDGE) { return false; } InstructionHandle h = edge.getSource().getLastInstruction(); if (h != null && h.getInstruction() instanceof IFNONNULL && isNullCheck(h, methodGen.getConstantPool())) { return true; } return false; }
/** * Return whether or not given instruction is an assertion. * * @param handle the instruction * @return true if instruction is an assertion, false otherwise */ private boolean isAssertion(InstructionHandle handle) { return assertionMethods.isAssertionHandle(handle, methodGen.getConstantPool()); }
/** * Check to see if the instruction has a null check associated with it, and if so, add a * dereference. * * @param location the Location of the instruction * @param vnaFrame ValueNumberFrame at the Location of the instruction * @param fact the dataflow value to modify * @throws DataflowAnalysisException */ private void checkInstance( Location location, ValueNumberFrame vnaFrame, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { // See if this instruction has a null check. // If it does, the fall through predecessor will be // identify itself as the null check. if (!location.isFirstInstructionInBasicBlock()) { return; } if (invDataflow == null) { return; } BasicBlock fallThroughPredecessor = cfg.getPredecessorWithEdgeType(location.getBasicBlock(), EdgeTypes.FALL_THROUGH_EDGE); if (fallThroughPredecessor == null || !fallThroughPredecessor.isNullCheck()) { return; } // Get the null-checked value ValueNumber vn = vnaFrame.getInstance(location.getHandle().getInstruction(), methodGen.getConstantPool()); // Ignore dereferences of this if (!methodGen.isStatic()) { ValueNumber v = vnaFrame.getValue(0); if (v.equals(vn)) { return; } } if (vn.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) { return; } IsNullValueFrame startFact = null; startFact = invDataflow.getStartFact(fallThroughPredecessor); if (!startFact.isValid()) { return; } int slot = startFact.getInstanceSlot( location.getHandle().getInstruction(), methodGen.getConstantPool()); if (!reportDereference(startFact, slot)) { return; } if (DEBUG) { System.out.println("FOUND GUARANTEED DEREFERENCE"); System.out.println("Load: " + vnaFrame.getLoad(vn)); System.out.println("Pred: " + fallThroughPredecessor); System.out.println("startFact: " + startFact); System.out.println("Location: " + location); System.out.println("Value number frame: " + vnaFrame); System.out.println("Dereferenced valueNumber: " + vn); System.out.println("invDataflow: " + startFact); System.out.println("IGNORE_DEREF_OF_NCP: " + IGNORE_DEREF_OF_NCP); } // Mark the value number as being dereferenced at this location fact.addDeref(vn, location); }
@Override public void transferInstruction( InstructionHandle handle, BasicBlock basicBlock, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { Instruction instruction = handle.getInstruction(); if (fact.isTop()) { return; } Location location = new Location(handle, basicBlock); // If this is a call to an assertion method, // change the dataflow value to be TOP. // We don't want to report future derefs that would // be guaranteed only if the assertion methods // returns normally. // TODO: at some point, evaluate whether we should revisit this if (isAssertion(handle) // || handle.getInstruction() instanceof ATHROW ) { if (DEBUG) { System.out.println("MAKING BOTTOM0 AT: " + location); } fact.clear(); return; } // Get value number frame ValueNumberFrame vnaFrame = vnaDataflow.getFactAtLocation(location); if (!vnaFrame.isValid()) { if (DEBUG) { System.out.println("MAKING TOP1 AT: " + location); } // Probably dead code. // Assume this location can't be reached. makeFactTop(fact); return; } if (isNullCheck(handle, methodGen.getConstantPool())) { handleNullCheck(location, vnaFrame, fact); } // Check for calls to a method that unconditionally dereferences // a parameter. Mark any such arguments as derefs. if (CHECK_CALLS && instruction instanceof InvokeInstruction) { checkUnconditionalDerefDatabase(location, vnaFrame, fact); } // If this is a method call instruction, // check to see if any of the parameters are @NonNull, // and treat them as dereferences. if (CHECK_ANNOTATIONS && instruction instanceof InvokeInstruction) { checkNonNullParams(location, vnaFrame, fact); } if (CHECK_ANNOTATIONS && instruction instanceof ARETURN) { XMethod thisMethod = XFactory.createXMethod(methodGen); checkNonNullReturnValue(thisMethod, location, vnaFrame, fact); } if (CHECK_ANNOTATIONS && (instruction instanceof PUTFIELD || instruction instanceof PUTSTATIC)) { checkNonNullPutField(location, vnaFrame, fact); } // Check to see if an instance value is dereferenced here checkInstance(location, vnaFrame, fact); /* if (false) { fact.cleanDerefSet(location, vnaFrame); }*/ if (DEBUG && fact.isTop()) { System.out.println("MAKING TOP2 At: " + location); } }
/** Constructor. */ MethodVisitor(MethodGen m, ClassVisitor c) { mg = m; cv = c; cp = mg.getConstantPool(); cm = cv.getMetrics(); }
/** * Whenever the outgoing frame situation of an InstructionContext changes, all its successors are * put [back] into the queue [as if they were unvisited]. The proof of termination is about the * existence of a fix point of frame merging. */ private void circulationPump( MethodGen m, ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev) { final Random random = new Random(); InstructionContextQueue icq = new InstructionContextQueue(); start.execute( vanillaFrame, new ArrayList<InstructionContext>(), icv, ev); // new ArrayList() <=> no Instruction was executed before // => Top-Level routine (no jsr call before) icq.add(start, new ArrayList<InstructionContext>()); // LOOP! while (!icq.isEmpty()) { InstructionContext u; ArrayList<InstructionContext> ec; if (!DEBUG) { int r = random.nextInt(icq.size()); u = icq.getIC(r); ec = icq.getEC(r); icq.remove(r); } else { u = icq.getIC(0); ec = icq.getEC(0); icq.remove(0); } @SuppressWarnings("unchecked") // ec is of type ArrayList<InstructionContext> ArrayList<InstructionContext> oldchain = (ArrayList<InstructionContext>) (ec.clone()); @SuppressWarnings("unchecked") // ec is of type ArrayList<InstructionContext> ArrayList<InstructionContext> newchain = (ArrayList<InstructionContext>) (ec.clone()); newchain.add(u); if ((u.getInstruction().getInstruction()) instanceof RET) { // System.err.println(u); // We can only follow _one_ successor, the one after the // JSR that was recently executed. RET ret = (RET) (u.getInstruction().getInstruction()); ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex()); InstructionContext theSuccessor = cfg.contextOf(t.getTarget()); // Sanity check InstructionContext lastJSR = null; int skip_jsr = 0; for (int ss = oldchain.size() - 1; ss >= 0; ss--) { if (skip_jsr < 0) { throw new AssertionViolatedException("More RET than JSR in execution chain?!"); } // System.err.println("+"+oldchain.get(ss)); if ((oldchain.get(ss)).getInstruction().getInstruction() instanceof JsrInstruction) { if (skip_jsr == 0) { lastJSR = oldchain.get(ss); break; } skip_jsr--; } if ((oldchain.get(ss)).getInstruction().getInstruction() instanceof RET) { skip_jsr++; } } if (lastJSR == null) { throw new AssertionViolatedException( "RET without a JSR before in ExecutionChain?! EC: '" + oldchain + "'."); } JsrInstruction jsr = (JsrInstruction) (lastJSR.getInstruction().getInstruction()); if (theSuccessor != (cfg.contextOf(jsr.physicalSuccessor()))) { throw new AssertionViolatedException( "RET '" + u.getInstruction() + "' info inconsistent: jump back to '" + theSuccessor + "' or '" + cfg.contextOf(jsr.physicalSuccessor()) + "'?"); } if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { @SuppressWarnings( "unchecked") // newchain is already of type ArrayList<InstructionContext> ArrayList<InstructionContext> newchainClone = (ArrayList<InstructionContext>) newchain.clone(); icq.add(theSuccessor, newchainClone); } } else { // "not a ret" // Normal successors. Add them to the queue of successors. InstructionContext[] succs = u.getSuccessors(); for (InstructionContext v : succs) { if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { @SuppressWarnings( "unchecked") // newchain is already of type ArrayList<InstructionContext> ArrayList<InstructionContext> newchainClone = (ArrayList<InstructionContext>) newchain.clone(); icq.add(v, newchainClone); } } } // end "not a ret" // Exception Handlers. Add them to the queue of successors. // [subroutines are never protected; mandated by JustIce] ExceptionHandler[] exc_hds = u.getExceptionHandlers(); for (ExceptionHandler exc_hd : exc_hds) { InstructionContext v = cfg.contextOf(exc_hd.getHandlerStart()); // TODO: the "oldchain" and "newchain" is used to determine the subroutine // we're in (by searching for the last JSR) by the InstructionContext // implementation. Therefore, we should not use this chain mechanism // when dealing with exception handlers. // Example: a JSR with an exception handler as its successor does not // mean we're in a subroutine if we go to the exception handler. // We should address this problem later; by now we simply "cut" the chain // by using an empty chain for the exception handlers. // if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack // (u.getOutFrame().getStack().maxStack(), (exc_hds[s].getExceptionType()==null? // Type.THROWABLE : exc_hds[s].getExceptionType())) ), newchain), icv, ev){ // icq.add(v, (ArrayList) newchain.clone()); if (v.execute( new Frame( u.getOutFrame(oldchain).getLocals(), new OperandStack( u.getOutFrame(oldchain).getStack().maxStack(), (exc_hd.getExceptionType() == null ? Type.THROWABLE : exc_hd.getExceptionType()))), new ArrayList<InstructionContext>(), icv, ev)) { icq.add(v, new ArrayList<InstructionContext>()); } } } // while (!icq.isEmpty()) END InstructionHandle ih = start.getInstruction(); do { if ((ih.getInstruction() instanceof ReturnInstruction) && (!(cfg.isDead(ih)))) { InstructionContext ic = cfg.contextOf(ih); Frame f = ic.getOutFrame( new ArrayList< InstructionContext>()); // TODO: This is buggy, we check only the top-level // return instructions this way. Maybe some maniac // returns from a method when in a subroutine? LocalVariables lvs = f.getLocals(); for (int i = 0; i < lvs.maxLocals(); i++) { if (lvs.get(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object in the local variables array '" + lvs + "'."); } } OperandStack os = f.getStack(); for (int i = 0; i < os.size(); i++) { if (os.peek(i) instanceof UninitializedObjectType) { this.addMessage( "Warning: ReturnInstruction '" + ic + "' may leave method with an uninitialized object on the operand stack '" + os + "'."); } } // see JVM $4.8.2 // TODO implement all based on stack Type returnedType = null; InstructionHandle ihPrev = null; ihPrev = ih.getPrev(); if (ihPrev != null) { if (ihPrev.getInstruction() instanceof InvokeInstruction) { returnedType = ((InvokeInstruction) ihPrev.getInstruction()).getType(m.getConstantPool()); } if (ihPrev.getInstruction() instanceof LoadInstruction) { int index = ((LoadInstruction) ihPrev.getInstruction()).getIndex(); returnedType = lvs.get(index); } if (ihPrev.getInstruction() instanceof GETFIELD) { returnedType = ((GETFIELD) ihPrev.getInstruction()).getType(m.getConstantPool()); } } if (returnedType != null) { if (returnedType instanceof ObjectType) { try { if (!((ObjectType) returnedType).isAssignmentCompatibleWith(m.getReturnType())) { throw new StructuralCodeConstraintException( "Returned type " + returnedType + " does not match Method's return type " + m.getReturnType()); } } catch (ClassNotFoundException e) { // dont know what do do now, so raise RuntimeException throw new RuntimeException(e); } } else if (!returnedType.equals(m.getReturnType())) { throw new StructuralCodeConstraintException( "Returned type " + returnedType + " does not match Method's return type " + m.getReturnType()); } } } } while ((ih = ih.getNext()) != null); }