Ejemplo n.º 1
0
  @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);
    }
  }
Ejemplo n.º 11
0
 /** Constructor. */
 MethodVisitor(MethodGen m, ClassVisitor c) {
   mg = m;
   cv = c;
   cp = mg.getConstantPool();
   cm = cv.getMetrics();
 }
Ejemplo n.º 12
0
  /**
   * 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);
  }