docs: public Frame<V>[] analyze(final String owner, final MethodNode m)
              throws AnalyzerException {
          if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
              frames = (Frame<V>[]) new Frame<?>[0];
              return frames;
          }
          n = m.instructions.size();
          insns = m.instructions;
          handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
          frames = (Frame<V>[]) new Frame<?>[n];
          subroutines = new Subroutine[n];
          queued = new boolean[n];
          queue = new int[n];
          top = 0;
  
          // computes exception handlers for each instruction
          for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
              TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
              int begin = insns.indexOf(tcb.start);
              int end = insns.indexOf(tcb.end);
              for (int j = begin; j < end; ++j) {
                  List<TryCatchBlockNode> insnHandlers = handlers[j];
                  if (insnHandlers == null) {
                      insnHandlers = new ArrayList<TryCatchBlockNode>();
                      handlers[j] = insnHandlers;
                  }
                  insnHandlers.add(tcb);
              }
          }
  
          // computes the subroutine for each instruction:
          Subroutine main = new Subroutine(null, m.maxLocals, null);
          List<AbstractInsnNode> subroutineCalls = new ArrayList<AbstractInsnNode>();
          Map<LabelNode, Subroutine> subroutineHeads = new HashMap<LabelNode, Subroutine>();
          findSubroutine(0, main, subroutineCalls);
          while (!subroutineCalls.isEmpty()) {
              JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
              Subroutine sub = subroutineHeads.get(jsr.label);
              if (sub == null) {
                  sub = new Subroutine(jsr.label, m.maxLocals, jsr);
                  subroutineHeads.put(jsr.label, sub);
                  findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
              } else {
                  sub.callers.add(jsr);
              }
          }
          for (int i = 0; i < n; ++i) {
              if (subroutines[i] != null && subroutines[i].start == null) {
                  subroutines[i] = null;
              }
          }
  
          // initializes the data structures for the control flow analysis
          Frame<V> current = newFrame(m.maxLocals, m.maxStack);
          Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
          current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
          Type[] args = Type.getArgumentTypes(m.desc);
          int local = 0;
          if ((m.access & ACC_STATIC) == 0) {
              Type ctype = Type.getObjectType(owner);
              current.setLocal(local++, interpreter.newValue(ctype));
          }
          for (int i = 0; i < args.length; ++i) {
              current.setLocal(local++, interpreter.newValue(args[i]));
              if (args[i].getSize() == 2) {
                  current.setLocal(local++, interpreter.newValue(null));
              }
          }
          while (local < m.maxLocals) {
              current.setLocal(local++, interpreter.newValue(null));
          }
          merge(0, current, null);
  
          init(owner, m);
  
          // control flow analysis
          while (top > 0) {
              int insn = queue[--top];
              Frame<V> f = frames[insn];
              Subroutine subroutine = subroutines[insn];
              queued[insn] = false;
  
              AbstractInsnNode insnNode = null;
              try {
                  insnNode = m.instructions.get(insn);
                  int insnOpcode = insnNode.getOpcode();
                  int insnType = insnNode.getType();
  
                  if (insnType == AbstractInsnNode.LABEL
                          || insnType == AbstractInsnNode.LINE
                          || insnType == AbstractInsnNode.FRAME) {
                      merge(insn + 1, f, subroutine);
                      newControlFlowEdge(insn, insn + 1);
                  } else {
                      current.init(f).execute(insnNode, interpreter);
                      subroutine = subroutine == null ? null : subroutine.copy();
  
                      if (insnNode instanceof JumpInsnNode) {
                          JumpInsnNode j = (JumpInsnNode) insnNode;
                          if (insnOpcode != GOTO && insnOpcode != JSR) {
                              merge(insn + 1, current, subroutine);
                              newControlFlowEdge(insn, insn + 1);
                          }
                          int jump = insns.indexOf(j.label);
                          if (insnOpcode == JSR) {
                              merge(jump, current, new Subroutine(j.label,
                                      m.maxLocals, j));
                          } else {
                              merge(jump, current, subroutine);
                          }
                          newControlFlowEdge(insn, jump);
                      } else if (insnNode instanceof LookupSwitchInsnNode) {
                          LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
                          int jump = insns.indexOf(lsi.dflt);
                          merge(jump, current, subroutine);
                          newControlFlowEdge(insn, jump);
                          for (int j = 0; j < lsi.labels.size(); ++j) {
                              LabelNode label = lsi.labels.get(j);
                              jump = insns.indexOf(label);
                              merge(jump, current, subroutine);
                              newControlFlowEdge(insn, jump);
                          }
                      } else if (insnNode instanceof TableSwitchInsnNode) {
                          TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
                          int jump = insns.indexOf(tsi.dflt);
                          merge(jump, current, subroutine);
                          newControlFlowEdge(insn, jump);
                          for (int j = 0; j < tsi.labels.size(); ++j) {
                              LabelNode label = tsi.labels.get(j);
                              jump = insns.indexOf(label);
                              merge(jump, current, subroutine);
                              newControlFlowEdge(insn, jump);
                          }
                      } else if (insnOpcode == RET) {
                          if (subroutine == null) {
                              throw new AnalyzerException(insnNode,
                                      "RET instruction outside of a sub routine");
                          }
                          for (int i = 0; i < subroutine.callers.size(); ++i) {
                              JumpInsnNode caller = subroutine.callers.get(i);
                              int call = insns.indexOf(caller);
                              if (frames[call] != null) {
                                  merge(call + 1, frames[call], current,
                                          subroutines[call], subroutine.access);
                                  newControlFlowEdge(insn, call + 1);
                              }
                          }
                      } else if (insnOpcode != ATHROW
                              && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
                          if (subroutine != null) {
                              if (insnNode instanceof VarInsnNode) {
                                  int var = ((VarInsnNode) insnNode).var;
                                  subroutine.access[var] = true;
                                  if (insnOpcode == LLOAD || insnOpcode == DLOAD
                                          || insnOpcode == LSTORE
                                          || insnOpcode == DSTORE) {
                                      subroutine.access[var + 1] = true;
                                  }
                              } else if (insnNode instanceof IincInsnNode) {
                                  int var = ((IincInsnNode) insnNode).var;
                                  subroutine.access[var] = true;
                              }
                          }
                          merge(insn + 1, current, subroutine);
                          newControlFlowEdge(insn, insn + 1);
                      }
                  }
  
                  List<TryCatchBlockNode> insnHandlers = handlers[insn];
                  if (insnHandlers != null) {
                      for (int i = 0; i < insnHandlers.size(); ++i) {
                          TryCatchBlockNode tcb = insnHandlers.get(i);
                          Type type;
                          if (tcb.type == null) {
                              type = Type.getObjectType("java/lang/Throwable");
                          } else {
                              type = Type.getObjectType(tcb.type);
                          }
                          int jump = insns.indexOf(tcb.handler);
                          if (newControlFlowExceptionEdge(insn, tcb)) {
                              handler.init(f);
                              handler.clearStack();
                              handler.push(interpreter.newValue(type));
                              merge(jump, handler, subroutine);
                          }
                      }
                  }
              } catch (AnalyzerException e) {
                  throw new AnalyzerException(e.node, "Error at instruction "
                          + insn + ": " + e.getMessage(), e);
              } catch (Exception e) {
                  throw new AnalyzerException(insnNode, "Error at instruction "
                          + insn + ": " + e.getMessage(), e);
              }
          }
  
          return frames;
      }