private void doVisitMethodInsn(
     int opcode, final String owner, final String name, final String desc, final boolean itf) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 5);
   if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
     checkMethodIdentifier(version, name, "name");
   }
   checkInternalName(owner, "owner");
   checkMethodDesc(desc);
   if (opcode == Opcodes.INVOKEVIRTUAL && itf) {
     throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces");
   }
   if (opcode == Opcodes.INVOKEINTERFACE && !itf) {
     throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes");
   }
   // Calling super.visitMethodInsn requires to call the correct version
   // depending on this.api (otherwise infinite loops can occur). To
   // simplify and to make it easier to automatically remove the backward
   // compatibility code, we inline the code of the overridden method here.
   if (mv != null) {
     mv.visitMethodInsn(opcode, owner, name, desc, itf);
   }
   ++insnCount;
 }
 @Override
 public void visitLdcInsn(final Object cst) {
   checkStartCode();
   checkEndCode();
   checkLDCConstant(cst);
   super.visitLdcInsn(cst);
   ++insnCount;
 }
 @Override
 public void visitLineNumber(final int line, final Label start) {
   checkStartCode();
   checkEndCode();
   checkUnsignedShort(line, "Invalid line number");
   checkLabel(start, true, "start label");
   super.visitLineNumber(line, start);
 }
 @Override
 public void visitInsn(final int opcode) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 0);
   super.visitInsn(opcode);
   ++insnCount;
 }
 @Override
 public void visitVarInsn(final int opcode, final int var) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 2);
   checkUnsignedShort(var, "Invalid variable index");
   super.visitVarInsn(opcode, var);
   ++insnCount;
 }
 @Override
 public void visitIincInsn(final int var, final int increment) {
   checkStartCode();
   checkEndCode();
   checkUnsignedShort(var, "Invalid variable index");
   checkSignedShort(increment, "Invalid increment");
   super.visitIincInsn(var, increment);
   ++insnCount;
 }
 @Override
 public void visitLabel(final Label label) {
   checkStartCode();
   checkEndCode();
   checkLabel(label, false, "label");
   if (labels.get(label) != null) {
     throw new IllegalArgumentException("Already visited label");
   }
   labels.put(label, insnCount);
   super.visitLabel(label);
 }
 @Override
 public void visitJumpInsn(final int opcode, final Label label) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 6);
   checkLabel(label, false, "label");
   checkNonDebugLabel(label);
   super.visitJumpInsn(opcode, label);
   usedLabels.add(label);
   ++insnCount;
 }
 @Override
 public void visitTypeInsn(final int opcode, final String type) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 3);
   checkInternalName(type, "type");
   if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
     throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type);
   }
   super.visitTypeInsn(opcode, type);
   ++insnCount;
 }
 @Override
 public void visitFieldInsn(
     final int opcode, final String owner, final String name, final String desc) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 4);
   checkInternalName(owner, "owner");
   checkUnqualifiedName(version, name, "name");
   checkDesc(desc, false);
   super.visitFieldInsn(opcode, owner, name, desc);
   ++insnCount;
 }
 @Override
 public AnnotationVisitor visitTryCatchAnnotation(
     final int typeRef, final TypePath typePath, final String desc, final boolean visible) {
   checkStartCode();
   checkEndCode();
   int sort = typeRef >>> 24;
   if (sort != TypeReference.EXCEPTION_PARAMETER) {
     throw new IllegalArgumentException(
         "Invalid type reference sort 0x" + Integer.toHexString(sort));
   }
   CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
   CheckMethodAdapter.checkDesc(desc, false);
   return new CheckAnnotationAdapter(
       super.visitTryCatchAnnotation(typeRef, typePath, desc, visible));
 }
 @Override
 public void visitMultiANewArrayInsn(final String desc, final int dims) {
   checkStartCode();
   checkEndCode();
   checkDesc(desc, false);
   if (desc.charAt(0) != '[') {
     throw new IllegalArgumentException(
         "Invalid descriptor (must be an array type descriptor): " + desc);
   }
   if (dims < 1) {
     throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): " + dims);
   }
   if (dims > desc.lastIndexOf('[') + 1) {
     throw new IllegalArgumentException(
         "Invalid dimensions (must not be greater than dims(desc)): " + dims);
   }
   super.visitMultiANewArrayInsn(desc, dims);
   ++insnCount;
 }
 @Override
 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
   checkEndCode();
   checkStartCode();
   checkLabel(dflt, false, "default label");
   checkNonDebugLabel(dflt);
   if (keys == null || labels == null || keys.length != labels.length) {
     throw new IllegalArgumentException("There must be the same number of keys and labels");
   }
   for (int i = 0; i < labels.length; ++i) {
     checkLabel(labels[i], false, "label at index " + i);
     checkNonDebugLabel(labels[i]);
   }
   super.visitLookupSwitchInsn(dflt, keys, labels);
   usedLabels.add(dflt);
   for (int i = 0; i < labels.length; ++i) {
     usedLabels.add(labels[i]);
   }
   ++insnCount;
 }
 @Override
 public void visitTryCatchBlock(
     final Label start, final Label end, final Label handler, final String type) {
   checkStartCode();
   checkEndCode();
   checkLabel(start, false, "start label");
   checkLabel(end, false, "end label");
   checkLabel(handler, false, "handler label");
   checkNonDebugLabel(start);
   checkNonDebugLabel(end);
   checkNonDebugLabel(handler);
   if (labels.get(start) != null || labels.get(end) != null || labels.get(handler) != null) {
     throw new IllegalStateException("Try catch blocks must be visited before their labels");
   }
   if (type != null) {
     checkInternalName(type, "type");
   }
   super.visitTryCatchBlock(start, end, handler, type);
   handlers.add(start);
   handlers.add(end);
 }
 @Override
 public AnnotationVisitor visitLocalVariableAnnotation(
     int typeRef,
     TypePath typePath,
     Label[] start,
     Label[] end,
     int[] index,
     String desc,
     boolean visible) {
   checkStartCode();
   checkEndCode();
   int sort = typeRef >>> 24;
   if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) {
     throw new IllegalArgumentException(
         "Invalid type reference sort 0x" + Integer.toHexString(sort));
   }
   CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
   checkDesc(desc, false);
   if (start == null
       || end == null
       || index == null
       || end.length != start.length
       || index.length != start.length) {
     throw new IllegalArgumentException(
         "Invalid start, end and index arrays (must be non null and of identical length");
   }
   for (int i = 0; i < start.length; ++i) {
     checkLabel(start[i], true, "start label");
     checkLabel(end[i], true, "end label");
     checkUnsignedShort(index[i], "Invalid variable index");
     int s = labels.get(start[i]).intValue();
     int e = labels.get(end[i]).intValue();
     if (e < s) {
       throw new IllegalArgumentException(
           "Invalid start and end labels (end must be greater than start)");
     }
   }
   return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible);
 }
 @Override
 public AnnotationVisitor visitInsnAnnotation(
     final int typeRef, final TypePath typePath, final String desc, final boolean visible) {
   checkStartCode();
   checkEndCode();
   int sort = typeRef >>> 24;
   if (sort != TypeReference.INSTANCEOF
       && sort != TypeReference.NEW
       && sort != TypeReference.CONSTRUCTOR_REFERENCE
       && sort != TypeReference.METHOD_REFERENCE
       && sort != TypeReference.CAST
       && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
       && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT
       && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
       && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) {
     throw new IllegalArgumentException(
         "Invalid type reference sort 0x" + Integer.toHexString(sort));
   }
   CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
   CheckMethodAdapter.checkDesc(desc, false);
   return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, typePath, desc, visible));
 }
 @Override
 public void visitIntInsn(final int opcode, final int operand) {
   checkStartCode();
   checkEndCode();
   checkOpcode(opcode, 1);
   switch (opcode) {
     case Opcodes.BIPUSH:
       checkSignedByte(operand, "Invalid operand");
       break;
     case Opcodes.SIPUSH:
       checkSignedShort(operand, "Invalid operand");
       break;
       // case Constants.NEWARRAY:
     default:
       if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
         throw new IllegalArgumentException(
             "Invalid operand (must be an array type code T_...): " + operand);
       }
   }
   super.visitIntInsn(opcode, operand);
   ++insnCount;
 }
 @Override
 public void visitLocalVariable(
     final String name,
     final String desc,
     final String signature,
     final Label start,
     final Label end,
     final int index) {
   checkStartCode();
   checkEndCode();
   checkUnqualifiedName(version, name, "name");
   checkDesc(desc, false);
   checkLabel(start, true, "start label");
   checkLabel(end, true, "end label");
   checkUnsignedShort(index, "Invalid variable index");
   int s = labels.get(start).intValue();
   int e = labels.get(end).intValue();
   if (e < s) {
     throw new IllegalArgumentException(
         "Invalid start and end labels (end must be greater than start)");
   }
   super.visitLocalVariable(name, desc, signature, start, end, index);
 }
 @Override
 public void visitMaxs(final int maxStack, final int maxLocals) {
   checkStartCode();
   checkEndCode();
   endCode = true;
   for (Label l : usedLabels) {
     if (labels.get(l) == null) {
       throw new IllegalStateException("Undefined label used");
     }
   }
   for (int i = 0; i < handlers.size(); ) {
     Integer start = labels.get(handlers.get(i++));
     Integer end = labels.get(handlers.get(i++));
     if (start == null || end == null) {
       throw new IllegalStateException("Undefined try catch block labels");
     }
     if (end.intValue() <= start.intValue()) {
       throw new IllegalStateException("Emty try catch block handler range");
     }
   }
   checkUnsignedShort(maxStack, "Invalid max stack");
   checkUnsignedShort(maxLocals, "Invalid max locals");
   super.visitMaxs(maxStack, maxLocals);
 }
 @Override
 public void visitTableSwitchInsn(
     final int min, final int max, final Label dflt, final Label... labels) {
   checkStartCode();
   checkEndCode();
   if (max < min) {
     throw new IllegalArgumentException(
         "Max = " + max + " must be greater than or equal to min = " + min);
   }
   checkLabel(dflt, false, "default label");
   checkNonDebugLabel(dflt);
   if (labels == null || labels.length != max - min + 1) {
     throw new IllegalArgumentException("There must be max - min + 1 labels");
   }
   for (int i = 0; i < labels.length; ++i) {
     checkLabel(labels[i], false, "label at index " + i);
     checkNonDebugLabel(labels[i]);
   }
   super.visitTableSwitchInsn(min, max, dflt, labels);
   for (int i = 0; i < labels.length; ++i) {
     usedLabels.add(labels[i]);
   }
   ++insnCount;
 }
 @Override
 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
   checkStartCode();
   checkEndCode();
   checkMethodIdentifier(version, name, "name");
   checkMethodDesc(desc);
   if (bsm.getTag() != Opcodes.H_INVOKESTATIC && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
     throw new IllegalArgumentException("invalid handle tag " + bsm.getTag());
   }
   for (int i = 0; i < bsmArgs.length; i++) {
     checkLDCConstant(bsmArgs[i]);
   }
   super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
   ++insnCount;
 }