void setExecuted(ThreadInfo ti, Instruction insn) {
      int idx = ti.getId();

      if (covered == null) {
        covered = new BitSet[idx + 1];
      } else if (idx >= covered.length) {
        BitSet[] a = new BitSet[idx + 1];
        System.arraycopy(covered, 0, a, 0, covered.length);
        covered = a;
      }

      if (covered[idx] == null) {
        covered[idx] = new BitSet(mi.getInstructions().length);
      }

      int off = insn.getInstructionIndex();
      covered[idx].set(off);

      if (showBranchCoverage && (insn instanceof IfInstruction)) {
        if (branchTrue == null) {
          branchTrue = new BitSet(mi.getInstructions().length);
          branchFalse = new BitSet(branchTrue.size());
        }
        if (!((IfInstruction) insn).getConditionValue()) {
          branchTrue.set(off);
        } else {
          branchFalse.set(off);
        }
      }
    }
    BitSet getHandlers() {
      // this algorithm is a bit subtle - we walk through the code until
      // we hit a forward GOTO (or RETURN). If the insn following the goto is the
      // beginning of a handler, we mark everything up to the jump address
      // as a handler block

      if (handlers == null) {
        BitSet hs = getHandlerStarts();
        Instruction[] code = mi.getInstructions();
        BitSet b = new BitSet(code.length);

        if (!hs.isEmpty()) {
          for (int i = 0; i < code.length; i++) {
            Instruction insn = code[i];
            if (insn instanceof GOTO) {
              GOTO gotoInsn = (GOTO) insn;
              if (!gotoInsn.isBackJump() && hs.get(i + 1)) { // jump around handler
                int handlerEnd = gotoInsn.getTarget().getInstructionIndex();
                for (i++; i < handlerEnd; i++) {
                  b.set(i);
                }
              }
            } else if (insn instanceof ReturnInstruction) { // everything else is handler
              for (i++; i < code.length; i++) {
                b.set(i);
              }
            }
          }
        }

        handlers = b;
      }

      return handlers;
    }
    void printBodyCoverage(MethodCoverage mc) {
      MethodInfo mi = mc.getMethodInfo();
      Instruction[] code = mi.getInstructions();
      BitSet cov = mc.getExecutedInsn();
      int i, start = -1;

      BitSet handlers = mc.getHandlers();

      if (excludeHandlers) {
        cov.andNot(handlers);
      }

      for (i = 0; i < code.length; i++) {
        if (!cov.get(i)) { // not covered
          if (start == -1) {
            start = i;
          }
        } else { // covered
          if (start != -1) {
            printSourceRange(code, handlers, start, i - 1, "");
            start = -1;
          }
        }
      }
      if (start != -1) {
        printSourceRange(code, handlers, start, i - 1, "");
      }

      // now print the missing branches
      BitSet branches = mc.getBranches();
      lastStart = -1; // reset in case condition and branch are in same line
      for (i = 0; i < code.length; i++) {
        if (branches.get(i)) {
          String prefix = "";
          BitSet bTrue = mc.branchTrue;
          BitSet bFalse = mc.branchFalse;
          if (bTrue != null) { // means we have condition bit sets
            boolean cTrue = bTrue.get(i);
            boolean cFalse = bFalse.get(i);
            if (cTrue) {
              prefix = cFalse ? "" : "F "; // covered or false missing
            } else {
              prefix = cFalse ? "T " : "N "; // true or both missing
            }
          } else {
            prefix = "N "; // not covered at all
          }

          if (prefix != null) {
            printSourceRange(code, handlers, i, i, prefix);
          }
        }
      }
    }
    BitSet getBasicBlocks() {
      if (basicBlocks == null) {
        Instruction[] code = mi.getInstructions();
        BitSet bb = new BitSet(code.length);

        bb.set(0); // first insn is always a bb start

        // first, look at the insn type
        for (int i = 0; i < code.length; i++) {
          Instruction insn = code[i];
          if (insn instanceof IfInstruction) {
            IfInstruction ifInsn = (IfInstruction) insn;

            Instruction tgt = ifInsn.getTarget();
            bb.set(tgt.getInstructionIndex());

            tgt = ifInsn.getNext();
            bb.set(tgt.getInstructionIndex());
          } else if (insn instanceof GOTO) {
            Instruction tgt = ((GOTO) insn).getTarget();
            bb.set(tgt.getInstructionIndex());
          } else if (insn instanceof InvokeInstruction) {
            // hmm, this might be a bit too conservative, but who says we
            // don't jump out of a caller into a handler, or even that we
            // ever return from the call?
            Instruction tgt = insn.getNext();
            bb.set(tgt.getInstructionIndex());
          }
        }

        // and now look at the handlers (every first insn is a bb start)
        ExceptionHandler[] handlers = mi.getExceptions();
        if (handlers != null) {
          for (int i = 0; i < handlers.length; i++) {
            Instruction tgt = mi.getInstructionAt(handlers[i].getHandler());
            bb.set(tgt.getInstructionIndex());
          }
        }

        basicBlocks = bb;

        /** dump
         * System.out.println();
         * System.out.println(mi.getFullName());
         * for (int i=0; i<code.length; i++) {
         * System.out.print(String.format("%1$2d %2$c ",i, bb.get(i) ? '>' : ' '));
         * System.out.println(code[i]);
         * }
         **/
      }

      return basicBlocks;
    }
    BitSet getHandlerStarts() {
      BitSet b = new BitSet(mi.getInstructions().length);
      ExceptionHandler[] handler = mi.getExceptions();

      if (handler != null) {
        for (int i = 0; i < handler.length; i++) {
          Instruction hs = mi.getInstructionAt(handler[i].getHandler());
          b.set(hs.getInstructionIndex());
        }
      }

      return b;
    }
    BitSet getExecutedInsn() {
      int nTotal = mi.getInstructions().length;
      BitSet bUnion = new BitSet(nTotal);

      if (covered != null) {
        for (BitSet b : covered) {
          if (b != null) {
            bUnion.or(b);
          }
        }
      }

      return bUnion;
    }
    // that's kind of redundant with basic blocks, but not really - here
    // we are interested in the logic behind branches, i.e. we want to know
    // what the condition values were. We are not interested in GOTOs and exceptions
    BitSet getBranches() {
      if (branches == null) {
        Instruction[] code = mi.getInstructions();
        BitSet br = new BitSet(code.length);

        for (int i = 0; i < code.length; i++) {
          Instruction insn = code[i];
          if (insn instanceof IfInstruction) {
            br.set(i);
          }
        }

        branches = br;
      }

      return branches;
    }
    boolean getCoveredLines(BitSet executable, BitSet covered) {
      Instruction inst[] = mi.getInstructions();
      BitSet insn;
      int i, line;

      if (covered == null) {
        return false;
      }

      insn = getExecutedInsn();
      if (excludeHandlers) {
        insn.andNot(getHandlers());
      }

      if (branchTrue != null) {
        for (i = branchTrue.length() - 1; i >= 0; i--) {
          boolean cTrue = branchTrue.get(i);
          boolean cFalse = branchFalse.get(i);

          if ((!cTrue || !cFalse) && (inst[i] instanceof IfInstruction)) {
            insn.clear(i);
          }
        }
      }

      for (i = inst.length - 1; i >= 0; i--) {
        line = inst[i].getLineNumber();

        if (line > 0) {
          executable.set(line);
          covered.set(line);
        }
      }

      for (i = inst.length - 1; i >= 0; i--) {
        line = inst[i].getLineNumber();
        if ((!insn.get(i))
            && (line
                > 0)) { // TODO What do we do with instructions that don't have a line number.  Is
          // this even possible?
          covered.clear(line);
        }
      }

      return true;
    }
    Coverage getCoveredInsn() {
      int nTotal = mi.getInstructions().length;

      if (excludeHandlers) {
        nTotal -= getHandlers().cardinality();
      }

      if (covered != null) {
        BitSet bExec = getExecutedInsn();
        if (excludeHandlers) {
          bExec.andNot(getHandlers());
        }
        return new Coverage(nTotal, bExec.cardinality());
      } else {
        return new Coverage(nTotal, 0);
      }
    }