docs: private void readCode(final MethodVisitor mv, final Context context, int u) {
          // reads the header
          byte[] b = this.b;
          char[] c = context.buffer;
          int maxStack = readUnsignedShort(u);
          int maxLocals = readUnsignedShort(u + 2);
          int codeLength = readInt(u + 4);
          u += 8;
  
          // reads the bytecode to find the labels
          int codeStart = u;
          int codeEnd = u + codeLength;
          Label[] labels = context.labels = new Label[codeLength + 2];
          createLabel(codeLength + 1, labels);
          while (u < codeEnd) {
              int offset = u - codeStart;
              int opcode = b[u] & 0xFF;
              switch (ClassWriter.TYPE[opcode]) {
              case ClassWriter.NOARG_INSN:
              case ClassWriter.IMPLVAR_INSN:
                  u += 1;
                  break;
              case ClassWriter.LABEL_INSN:
                  createLabel(offset + readShort(u + 1), labels);
                  u += 3;
                  break;
              case ClassWriter.ASM_LABEL_INSN:
                  createLabel(offset + readUnsignedShort(u + 1), labels);
                  u += 3;
                  break;
              case ClassWriter.LABELW_INSN:
              case ClassWriter.ASM_LABELW_INSN:
                  createLabel(offset + readInt(u + 1), labels);
                  u += 5;
                  break;
              case ClassWriter.WIDE_INSN:
                  opcode = b[u + 1] & 0xFF;
                  if (opcode == Opcodes.IINC) {
                      u += 6;
                  } else {
                      u += 4;
                  }
                  break;
              case ClassWriter.TABL_INSN:
                  // skips 0 to 3 padding bytes
                  u = u + 4 - (offset & 3);
                  // reads instruction
                  createLabel(offset + readInt(u), labels);
                  for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) {
                      createLabel(offset + readInt(u + 12), labels);
                      u += 4;
                  }
                  u += 12;
                  break;
              case ClassWriter.LOOK_INSN:
                  // skips 0 to 3 padding bytes
                  u = u + 4 - (offset & 3);
                  // reads instruction
                  createLabel(offset + readInt(u), labels);
                  for (int i = readInt(u + 4); i > 0; --i) {
                      createLabel(offset + readInt(u + 12), labels);
                      u += 8;
                  }
                  u += 8;
                  break;
              case ClassWriter.VAR_INSN:
              case ClassWriter.SBYTE_INSN:
              case ClassWriter.LDC_INSN:
                  u += 2;
                  break;
              case ClassWriter.SHORT_INSN:
              case ClassWriter.LDCW_INSN:
              case ClassWriter.FIELDORMETH_INSN:
              case ClassWriter.TYPE_INSN:
              case ClassWriter.IINC_INSN:
                  u += 3;
                  break;
              case ClassWriter.ITFMETH_INSN:
              case ClassWriter.INDYMETH_INSN:
                  u += 5;
                  break;
              // case MANA_INSN:
              default:
                  u += 4;
                  break;
              }
          }
  
          // reads the try catch entries to find the labels, and also visits them
          for (int i = readUnsignedShort(u); i > 0; --i) {
              Label start = createLabel(readUnsignedShort(u + 2), labels);
              Label end = createLabel(readUnsignedShort(u + 4), labels);
              Label handler = createLabel(readUnsignedShort(u + 6), labels);
              String type = readUTF8(items[readUnsignedShort(u + 8)], c);
              mv.visitTryCatchBlock(start, end, handler, type);
              u += 8;
          }
          u += 2;
  
          // reads the code attributes
          int[] tanns = null; // start index of each visible type annotation
          int[] itanns = null; // start index of each invisible type annotation
          int tann = 0; // current index in tanns array
          int itann = 0; // current index in itanns array
          int ntoff = -1; // next visible type annotation code offset
          int nitoff = -1; // next invisible type annotation code offset
          int varTable = 0;
          int varTypeTable = 0;
          boolean zip = true;
          boolean unzip = (context.flags & EXPAND_FRAMES) != 0;
          int stackMap = 0;
          int stackMapSize = 0;
          int frameCount = 0;
          Context frame = null;
          Attribute attributes = null;
  
          for (int i = readUnsignedShort(u); i > 0; --i) {
              String attrName = readUTF8(u + 2, c);
              if ("LocalVariableTable".equals(attrName)) {
                  if ((context.flags & SKIP_DEBUG) == 0) {
                      varTable = u + 8;
                      for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
                          int label = readUnsignedShort(v + 10);
                          createDebugLabel(label, labels);
                          label += readUnsignedShort(v + 12);
                          createDebugLabel(label, labels);
                          v += 10;
                      }
                  }
              } else if ("LocalVariableTypeTable".equals(attrName)) {
                  varTypeTable = u + 8;
              } else if ("LineNumberTable".equals(attrName)) {
                  if ((context.flags & SKIP_DEBUG) == 0) {
                      for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
                          int label = readUnsignedShort(v + 10);
                          createDebugLabel(label, labels);
                          Label l = labels[label];
                          while (l.line > 0) {
                              if (l.next == null) {
                                  l.next = new Label();
                              }
                              l = l.next;
                          }
                          l.line = readUnsignedShort(v + 12);
                          v += 4;
                      }
                  }
              } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
                  tanns = readTypeAnnotations(mv, context, u + 8, true);
                  ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1
                          : readUnsignedShort(tanns[0] + 1);
              } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
                  itanns = readTypeAnnotations(mv, context, u + 8, false);
                  nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1
                          : readUnsignedShort(itanns[0] + 1);
              } else if ("StackMapTable".equals(attrName)) {
                  if ((context.flags & SKIP_FRAMES) == 0) {
                      stackMap = u + 10;
                      stackMapSize = readInt(u + 4);
                      frameCount = readUnsignedShort(u + 8);
                  }
                  /*
                   * here we do not extract the labels corresponding to the
                   * attribute content. This would require a full parsing of the
                   * attribute, which would need to be repeated in the second
                   * phase (see below). Instead the content of the attribute is
                   * read one frame at a time (i.e. after a frame has been
                   * visited, the next frame is read), and the labels it contains
                   * are also extracted one frame at a time. Thanks to the
                   * ordering of frames, having only a "one frame lookahead" is
                   * not a problem, i.e. it is not possible to see an offset
                   * smaller than the offset of the current insn and for which no
                   * Label exist.
                   */
                  /*
                   * This is not true for UNINITIALIZED type offsets. We solve
                   * this by parsing the stack map table without a full decoding
                   * (see below).
                   */
              } else if ("StackMap".equals(attrName)) {
                  if ((context.flags & SKIP_FRAMES) == 0) {
                      zip = false;
                      stackMap = u + 10;
                      stackMapSize = readInt(u + 4);
                      frameCount = readUnsignedShort(u + 8);
                  }
                  /*
                   * IMPORTANT! here we assume that the frames are ordered, as in
                   * the StackMapTable attribute, although this is not guaranteed
                   * by the attribute format.
                   */
              } else {
                  for (int j = 0; j < context.attrs.length; ++j) {
                      if (context.attrs[j].type.equals(attrName)) {
                          Attribute attr = context.attrs[j].read(this, u + 8,
                                  readInt(u + 4), c, codeStart - 8, labels);
                          if (attr != null) {
                              attr.next = attributes;
                              attributes = attr;
                          }
                      }
                  }
              }
              u += 6 + readInt(u + 4);
          }
          u += 2;
  
          // generates the first (implicit) stack map frame
          if (stackMap != 0) {
              /*
               * for the first explicit frame the offset is not offset_delta + 1
               * but only offset_delta; setting the implicit frame offset to -1
               * allow the use of the "offset_delta + 1" rule in all cases
               */
              frame = context;
              frame.offset = -1;
              frame.mode = 0;
              frame.localCount = 0;
              frame.localDiff = 0;
              frame.stackCount = 0;
              frame.local = new Object[maxLocals];
              frame.stack = new Object[maxStack];
              if (unzip) {
                  getImplicitFrame(context);
              }
              /*
               * Finds labels for UNINITIALIZED frame types. Instead of decoding
               * each element of the stack map table, we look for 3 consecutive
               * bytes that "look like" an UNINITIALIZED type (tag 8, offset
               * within code bounds, NEW instruction at this offset). We may find
               * false positives (i.e. not real UNINITIALIZED types), but this
               * should be rare, and the only consequence will be the creation of
               * an unneeded label. This is better than creating a label for each
               * NEW instruction, and faster than fully decoding the whole stack
               * map table.
               */
              for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) {
                  if (b[i] == 8) { // UNINITIALIZED FRAME TYPE
                      int v = readUnsignedShort(i + 1);
                      if (v >= 0 && v < codeLength) {
                          if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) {
                              createLabel(v, labels);
                          }
                      }
                  }
              }
          }
          if ((context.flags & EXPAND_ASM_INSNS) != 0
              && (context.flags & EXPAND_FRAMES) != 0) {
              // Expanding the ASM pseudo instructions can introduce F_INSERT
              // frames, even if the method does not currently have any frame.
              // Also these inserted frames must be computed by simulating the
              // effect of the bytecode instructions one by one, starting from the
              // first one and the last existing frame (or the implicit first
              // one). Finally, due to the way MethodWriter computes this (with
              // the compute = INSERTED_FRAMES option), MethodWriter needs to know
              // maxLocals before the first instruction is visited. For all these
              // reasons we always visit the implicit first frame in this case
              // (passing only maxLocals - the rest can be and is computed in
              // MethodWriter).
              mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
          }
  
          // visits the instructions
          int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
          boolean insertFrame = false;
          u = codeStart;
          while (u < codeEnd) {
              int offset = u - codeStart;
  
              // visits the label and line number for this offset, if any
              Label l = labels[offset];
              if (l != null) {
                  Label next = l.next;
                  l.next = null;
                  mv.visitLabel(l);
                  if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) {
                      mv.visitLineNumber(l.line, l);
                      while (next != null) {
                          mv.visitLineNumber(next.line, l);
                          next = next.next;
                      }
                  }
              }
  
              // visits the frame for this offset, if any
              while (frame != null
                      && (frame.offset == offset || frame.offset == -1)) {
                  // if there is a frame for this offset, makes the visitor visit
                  // it, and reads the next frame if there is one.
                  if (frame.offset != -1) {
                      if (!zip || unzip) {
                          mv.visitFrame(Opcodes.F_NEW, frame.localCount,
                                  frame.local, frame.stackCount, frame.stack);
                      } else {
                          mv.visitFrame(frame.mode, frame.localDiff, frame.local,
                                  frame.stackCount, frame.stack);
                      }
                      // if there is already a frame for this offset, there is no
                      // need to insert a new one.
                      insertFrame = false;
                  }
                  if (frameCount > 0) {
                      stackMap = readFrame(stackMap, zip, unzip, frame);
                      --frameCount;
                  } else {
                      frame = null;
                  }
              }
              // inserts a frame for this offset, if requested by setting
              // insertFrame to true during the previous iteration. The actual
              // frame content will be computed in MethodWriter.
              if (insertFrame) {
                  mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
                  insertFrame = false;
              }
  
              // visits the instruction at this offset
              int opcode = b[u] & 0xFF;
              switch (ClassWriter.TYPE[opcode]) {
              case ClassWriter.NOARG_INSN:
                  mv.visitInsn(opcode);
                  u += 1;
                  break;
              case ClassWriter.IMPLVAR_INSN:
                  if (opcode > Opcodes.ISTORE) {
                      opcode -= 59; // ISTORE_0
                      mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2),
                              opcode & 0x3);
                  } else {
                      opcode -= 26; // ILOAD_0
                      mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
                  }
                  u += 1;
                  break;
              case ClassWriter.LABEL_INSN:
                  mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]);
                  u += 3;
                  break;
              case ClassWriter.LABELW_INSN:
                  mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
                          + readInt(u + 1)]);
                  u += 5;
                  break;
              case ClassWriter.ASM_LABEL_INSN: {
                  // changes temporary opcodes 202 to 217 (inclusive), 218
                  // and 219 to IFEQ ... JSR (inclusive), IFNULL and
                  // IFNONNULL
                  opcode = opcode < 218 ? opcode - 49 : opcode - 20;
                  Label target = labels[offset + readUnsignedShort(u + 1)];
                  // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
                  // <l> with IFNOTxxx <L> GOTO_W <l> L:..., where IFNOTxxx is
                  // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
                  // and where <L> designates the instruction just after
                  // the GOTO_W.
                  if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
                      mv.visitJumpInsn(opcode + 33, target);
                  } else {
                      opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
                              : opcode ^ 1;
                      Label endif = createLabel(offset + 3, labels);
                      mv.visitJumpInsn(opcode, endif);
                      mv.visitJumpInsn(200, target); // GOTO_W
                      // endif designates the instruction just after GOTO_W,
                      // and is visited as part of the next instruction. Since
                      // it is a jump target, we need to insert a frame here.
                      insertFrame = true;
                  }
                  u += 3;
                  break;
              }
              case ClassWriter.ASM_LABELW_INSN: {
                  // replaces the pseudo GOTO_W instruction with a real one.
                  mv.visitJumpInsn(200, labels[offset + readInt(u + 1)]);
                  // The instruction just after is a jump target (because pseudo
                  // GOTO_W are used in patterns IFNOTxxx <L> GOTO_W <l> L:...,
                  // see MethodWriter), so we need to insert a frame here.
                  insertFrame = true;
                  u += 5;
                  break;
              }
              case ClassWriter.WIDE_INSN:
                  opcode = b[u + 1] & 0xFF;
                  if (opcode == Opcodes.IINC) {
                      mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4));
                      u += 6;
                  } else {
                      mv.visitVarInsn(opcode, readUnsignedShort(u + 2));
                      u += 4;
                  }
                  break;
              case ClassWriter.TABL_INSN: {
                  // skips 0 to 3 padding bytes
                  u = u + 4 - (offset & 3);
                  // reads instruction
                  int label = offset + readInt(u);
                  int min = readInt(u + 4);
                  int max = readInt(u + 8);
                  Label[] table = new Label[max - min + 1];
                  u += 12;
                  for (int i = 0; i < table.length; ++i) {
                      table[i] = labels[offset + readInt(u)];
                      u += 4;
                  }
                  mv.visitTableSwitchInsn(min, max, labels[label], table);
                  break;
              }
              case ClassWriter.LOOK_INSN: {
                  // skips 0 to 3 padding bytes
                  u = u + 4 - (offset & 3);
                  // reads instruction
                  int label = offset + readInt(u);
                  int len = readInt(u + 4);
                  int[] keys = new int[len];
                  Label[] values = new Label[len];
                  u += 8;
                  for (int i = 0; i < len; ++i) {
                      keys[i] = readInt(u);
                      values[i] = labels[offset + readInt(u + 4)];
                      u += 8;
                  }
                  mv.visitLookupSwitchInsn(labels[label], keys, values);
                  break;
              }
              case ClassWriter.VAR_INSN:
                  mv.visitVarInsn(opcode, b[u + 1] & 0xFF);
                  u += 2;
                  break;
              case ClassWriter.SBYTE_INSN:
                  mv.visitIntInsn(opcode, b[u + 1]);
                  u += 2;
                  break;
              case ClassWriter.SHORT_INSN:
                  mv.visitIntInsn(opcode, readShort(u + 1));
                  u += 3;
                  break;
              case ClassWriter.LDC_INSN:
                  mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c));
                  u += 2;
                  break;
              case ClassWriter.LDCW_INSN:
                  mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c));
                  u += 3;
                  break;
              case ClassWriter.FIELDORMETH_INSN:
              case ClassWriter.ITFMETH_INSN: {
                  int cpIndex = items[readUnsignedShort(u + 1)];
                  boolean itf = b[cpIndex - 1] == ClassWriter.IMETH;
                  String iowner = readClass(cpIndex, c);
                  cpIndex = items[readUnsignedShort(cpIndex + 2)];
                  String iname = readUTF8(cpIndex, c);
                  String idesc = readUTF8(cpIndex + 2, c);
                  if (opcode < Opcodes.INVOKEVIRTUAL) {
                      mv.visitFieldInsn(opcode, iowner, iname, idesc);
                  } else {
                      mv.visitMethodInsn(opcode, iowner, iname, idesc, itf);
                  }
                  if (opcode == Opcodes.INVOKEINTERFACE) {
                      u += 5;
                  } else {
                      u += 3;
                  }
                  break;
              }
              case ClassWriter.INDYMETH_INSN: {
                  int cpIndex = items[readUnsignedShort(u + 1)];
                  int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)];
                  Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c);
                  int bsmArgCount = readUnsignedShort(bsmIndex + 2);
                  Object[] bsmArgs = new Object[bsmArgCount];
                  bsmIndex += 4;
                  for (int i = 0; i < bsmArgCount; i++) {
                      bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c);
                      bsmIndex += 2;
                  }
                  cpIndex = items[readUnsignedShort(cpIndex + 2)];
                  String iname = readUTF8(cpIndex, c);
                  String idesc = readUTF8(cpIndex + 2, c);
                  mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs);
                  u += 5;
                  break;
              }
              case ClassWriter.TYPE_INSN:
                  mv.visitTypeInsn(opcode, readClass(u + 1, c));
                  u += 3;
                  break;
              case ClassWriter.IINC_INSN:
                  mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]);
                  u += 3;
                  break;
              // case MANA_INSN:
              default:
                  mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF);
                  u += 4;
                  break;
              }
  
              // visit the instruction annotations, if any
              while (tanns != null && tann < tanns.length && ntoff <= offset) {
                  if (ntoff == offset) {
                      int v = readAnnotationTarget(context, tanns[tann]);
                      readAnnotationValues(v + 2, c, true,
                              mv.visitInsnAnnotation(context.typeRef,
                                      context.typePath, readUTF8(v, c), true));
                  }
                  ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1
                          : readUnsignedShort(tanns[tann] + 1);
              }
              while (itanns != null && itann < itanns.length && nitoff <= offset) {
                  if (nitoff == offset) {
                      int v = readAnnotationTarget(context, itanns[itann]);
                      readAnnotationValues(v + 2, c, true,
                              mv.visitInsnAnnotation(context.typeRef,
                                      context.typePath, readUTF8(v, c), false));
                  }
                  nitoff = ++itann >= itanns.length
                          || readByte(itanns[itann]) < 0x43 ? -1
                          : readUnsignedShort(itanns[itann] + 1);
              }
          }
          if (labels[codeLength] != null) {
              mv.visitLabel(labels[codeLength]);
          }
  
          // visits the local variable tables
          if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) {
              int[] typeTable = null;
              if (varTypeTable != 0) {
                  u = varTypeTable + 2;
                  typeTable = new int[readUnsignedShort(varTypeTable) * 3];
                  for (int i = typeTable.length; i > 0;) {
                      typeTable[--i] = u + 6; // signature
                      typeTable[--i] = readUnsignedShort(u + 8); // index
                      typeTable[--i] = readUnsignedShort(u); // start
                      u += 10;
                  }
              }
              u = varTable + 2;
              for (int i = readUnsignedShort(varTable); i > 0; --i) {
                  int start = readUnsignedShort(u);
                  int length = readUnsignedShort(u + 2);
                  int index = readUnsignedShort(u + 8);
                  String vsignature = null;
                  if (typeTable != null) {
                      for (int j = 0; j < typeTable.length; j += 3) {
                          if (typeTable[j] == start && typeTable[j + 1] == index) {
                              vsignature = readUTF8(typeTable[j + 2], c);
                              break;
                          }
                      }
                  }
                  mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c),
                          vsignature, labels[start], labels[start + length],
                          index);
                  u += 10;
              }
          }
  
          // visits the local variables type annotations
          if (tanns != null) {
              for (int i = 0; i < tanns.length; ++i) {
                  if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) {
                      int v = readAnnotationTarget(context, tanns[i]);
                      v = readAnnotationValues(v + 2, c, true,
                              mv.visitLocalVariableAnnotation(context.typeRef,
                                      context.typePath, context.start,
                                      context.end, context.index, readUTF8(v, c),
                                      true));
                  }
              }
          }
          if (itanns != null) {
              for (int i = 0; i < itanns.length; ++i) {
                  if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) {
                      int v = readAnnotationTarget(context, itanns[i]);
                      v = readAnnotationValues(v + 2, c, true,
                              mv.visitLocalVariableAnnotation(context.typeRef,
                                      context.typePath, context.start,
                                      context.end, context.index, readUTF8(v, c),
                                      false));
                  }
              }
          }
  
          // visits the code attributes
          while (attributes != null) {
              Attribute attr = attributes.next;
              attributes.next = null;
              mv.visitAttribute(attributes);
              attributes = attr;
          }
  
          // visits the max stack and max locals values
          mv.visitMaxs(maxStack, maxLocals);
      }