private void processBlock(ByteBlock block, Frame frame, int[] workSet) {
    // Prepare the list of caught exceptions for this block.
    ByteCatchList catches = block.getCatches();
    machine.startBlock(catches.toRopCatchList());

    /*
     * Using a copy of the given frame, simulate each instruction,
     * calling into machine for each.
     */
    frame = frame.copy();
    sim.simulate(block, frame);
    frame.setImmutable();

    int extraBlockCount = machine.getExtraBlockCount();
    ArrayList<Insn> insns = machine.getInsns();
    int insnSz = insns.size();

    /*
     * Merge the frame into each possible non-exceptional
     * successor.
     */

    int catchSz = catches.size();
    IntList successors = block.getSuccessors();

    int startSuccessorIndex;

    Subroutine calledSubroutine = null;
    if (machine.hasJsr()) {
      /*
       * If this frame ends in a JSR, only merge our frame with
       * the subroutine start, not the subroutine's return target.
       */
      startSuccessorIndex = 1;

      int subroutineLabel = successors.get(1);

      if (subroutines[subroutineLabel] == null) {
        subroutines[subroutineLabel] = new Subroutine(subroutineLabel);
      }

      subroutines[subroutineLabel].addCallerBlock(block.getLabel());

      calledSubroutine = subroutines[subroutineLabel];
    } else if (machine.hasRet()) {
      /*
       * This block ends in a ret, which means it's the final block
       * in some subroutine. Ultimately, this block will be copied
       * and inlined for each call and then disposed of.
       */

      ReturnAddress ra = machine.getReturnAddress();
      int subroutineLabel = ra.getSubroutineAddress();

      if (subroutines[subroutineLabel] == null) {
        subroutines[subroutineLabel] = new Subroutine(subroutineLabel, block.getLabel());
      } else {
        subroutines[subroutineLabel].addRetBlock(block.getLabel());
      }

      successors = subroutines[subroutineLabel].getSuccessors();
      subroutines[subroutineLabel].mergeToSuccessors(frame, workSet);
      // Skip processing below since we just did it.
      startSuccessorIndex = successors.size();
    } else if (machine.wereCatchesUsed()) {
      /*
       * If there are catches, then the first successors
       * (which will either be all of them or all but the last one)
       * are catch targets.
       */
      startSuccessorIndex = catchSz;
    } else {
      startSuccessorIndex = 0;
    }

    int succSz = successors.size();
    for (int i = startSuccessorIndex; i < succSz; i++) {
      int succ = successors.get(i);
      try {
        mergeAndWorkAsNecessary(succ, block.getLabel(), calledSubroutine, frame, workSet);
      } catch (SimException ex) {
        ex.addContext("...while merging to block " + Hex.u2(succ));
        throw ex;
      }
    }

    if ((succSz == 0) && machine.returns()) {
      /*
       * The block originally contained a return, but it has
       * been made to instead end with a goto, and we need to
       * tell it at this point that its sole successor is the
       * return block. This has to happen after the merge loop
       * above, since, at this point, the return block doesn't
       * actually exist; it gets synthesized at the end of
       * processing the original blocks.
       */
      successors = IntList.makeImmutable(getSpecialLabel(RETURN));
      succSz = 1;
    }

    int primarySucc;

    if (succSz == 0) {
      primarySucc = -1;
    } else {
      primarySucc = machine.getPrimarySuccessorIndex();
      if (primarySucc >= 0) {
        primarySucc = successors.get(primarySucc);
      }
    }

    /*
     * This variable is true only when the method is synchronized and
     * the block being processed can possibly throw an exception.
     */
    boolean synch = isSynchronized() && machine.canThrow();

    if (synch || (catchSz != 0)) {
      /*
       * Deal with exception handlers: Merge an exception-catch
       * frame into each possible exception handler, and
       * construct a new set of successors to point at the
       * exception handler setup blocks (which get synthesized
       * at the very end of processing).
       */
      boolean catchesAny = false;
      IntList newSucc = new IntList(succSz);
      for (int i = 0; i < catchSz; i++) {
        ByteCatchList.Item one = catches.get(i);
        CstType exceptionClass = one.getExceptionClass();
        int targ = one.getHandlerPc();

        catchesAny |= (exceptionClass == CstType.OBJECT);

        Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass);

        try {
          mergeAndWorkAsNecessary(targ, block.getLabel(), null, f, workSet);
        } catch (SimException ex) {
          ex.addContext("...while merging exception to block " + Hex.u2(targ));
          throw ex;
        }

        /*
         * Set up the exception handler type.
         */
        CatchInfo handlers = catchInfos[targ];
        if (handlers == null) {
          handlers = new CatchInfo();
          catchInfos[targ] = handlers;
        }
        ExceptionHandlerSetup handler = handlers.getSetup(exceptionClass.getClassType());

        /*
         * The synthesized exception setup block will have the label given by handler.
         */
        newSucc.add(handler.getLabel());
      }

      if (synch && !catchesAny) {
        /*
         * The method is synchronized and this block doesn't
         * already have a catch-all handler, so add one to the
         * end, both in the successors and in the throwing
         * instruction(s) at the end of the block (which is where
         * the caught classes live).
         */
        newSucc.add(getSpecialLabel(SYNCH_CATCH_1));
        synchNeedsExceptionHandler = true;

        for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) {
          Insn insn = insns.get(i);
          if (insn.canThrow()) {
            insn = insn.withAddedCatch(Type.OBJECT);
            insns.set(i, insn);
          }
        }
      }

      if (primarySucc >= 0) {
        newSucc.add(primarySucc);
      }

      newSucc.setImmutable();
      successors = newSucc;
    }

    // Construct the final resulting block(s), and store it (them).

    int primarySuccListIndex = successors.indexOf(primarySucc);

    /*
     * If there are any extra blocks, work backwards through the
     * list of instructions, adding single-instruction blocks, and
     * resetting the successors variables as appropriate.
     */
    for (
    /*extraBlockCount*/ ; extraBlockCount > 0; extraBlockCount--) {
      /*
       * Some of the blocks that the RopperMachine wants added
       * are for move-result insns, and these need goto insns as well.
       */
      Insn extraInsn = insns.get(--insnSz);
      boolean needsGoto = extraInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE;
      InsnList il = new InsnList(needsGoto ? 2 : 1);
      IntList extraBlockSuccessors = successors;

      il.set(0, extraInsn);

      if (needsGoto) {
        il.set(1, new PlainInsn(Rops.GOTO, extraInsn.getPosition(), null, RegisterSpecList.EMPTY));
        /*
         * Obviously, this block won't be throwing an exception
         * so it should only have one successor.
         */
        extraBlockSuccessors = IntList.makeImmutable(primarySucc);
      }
      il.setImmutable();

      int label = getAvailableLabel();
      BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors, primarySucc);
      // All of these extra blocks will be in the same subroutine
      addBlock(bb, frame.getSubroutines());

      successors = successors.mutableCopy();
      successors.set(primarySuccListIndex, label);
      successors.setImmutable();
      primarySucc = label;
    }

    Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1);

    /*
     * Add a goto to the end of the block if it doesn't already
     * end with a branch, to maintain the invariant that all
     * blocks end with a branch of some sort or other. Note that
     * it is possible for there to be blocks for which no
     * instructions were ever output (e.g., only consist of pop*
     * in the original Java bytecode).
     */
    if ((lastInsn == null) || (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) {
      SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO : lastInsn.getPosition();
      insns.add(new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY));
      insnSz++;
    }

    /*
     * Construct a block for the remaining instructions (which in
     * the usual case is all of them).
     */

    InsnList il = new InsnList(insnSz);
    for (int i = 0; i < insnSz; i++) {
      il.set(i, insns.get(i));
    }
    il.setImmutable();

    BasicBlock bb = new BasicBlock(block.getLabel(), il, successors, primarySucc);
    addOrReplaceBlock(bb, frame.getSubroutines());
  }
Processes the given block.

@param block {@code non-null;} block to process
@param frame {@code non-null;} start frame for the block
@param workSet {@code non-null;} bits representing work to do, which this method may add to
android/android_framework/dex-maker/src/main/java/com/android/dx/cf/code/Ropper.java