private Constant parse0(int idx, BitSet wasUtf8) {
    Constant cst = pool.getOrNull(idx);
    if (cst != null) {
      return cst;
    }

    int at = offsets[idx];

    try {
      int tag = bytes.getUnsignedByte(at);
      switch (tag) {
        case CONSTANT_Utf8:
          {
            cst = parseUtf8(at);
            wasUtf8.set(idx);
            break;
          }
        case CONSTANT_Integer:
          {
            int value = bytes.getInt(at + 1);
            cst = CstInteger.make(value);
            break;
          }
        case CONSTANT_Float:
          {
            int bits = bytes.getInt(at + 1);
            cst = CstFloat.make(bits);
            break;
          }
        case CONSTANT_Long:
          {
            long value = bytes.getLong(at + 1);
            cst = CstLong.make(value);
            break;
          }
        case CONSTANT_Double:
          {
            long bits = bytes.getLong(at + 1);
            cst = CstDouble.make(bits);
            break;
          }
        case CONSTANT_Class:
          {
            int nameIndex = bytes.getUnsignedShort(at + 1);
            CstString name = (CstString) parse0(nameIndex, wasUtf8);
            cst = new CstType(Type.internClassName(name.getString()));
            break;
          }
        case CONSTANT_String:
          {
            int stringIndex = bytes.getUnsignedShort(at + 1);
            cst = parse0(stringIndex, wasUtf8);
            break;
          }
        case CONSTANT_Fieldref:
          {
            int classIndex = bytes.getUnsignedShort(at + 1);
            CstType type = (CstType) parse0(classIndex, wasUtf8);
            int natIndex = bytes.getUnsignedShort(at + 3);
            CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
            cst = new CstFieldRef(type, nat);
            break;
          }
        case CONSTANT_Methodref:
          {
            int classIndex = bytes.getUnsignedShort(at + 1);
            CstType type = (CstType) parse0(classIndex, wasUtf8);
            int natIndex = bytes.getUnsignedShort(at + 3);
            CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
            cst = new CstMethodRef(type, nat);
            break;
          }
        case CONSTANT_InterfaceMethodref:
          {
            int classIndex = bytes.getUnsignedShort(at + 1);
            CstType type = (CstType) parse0(classIndex, wasUtf8);
            int natIndex = bytes.getUnsignedShort(at + 3);
            CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
            cst = new CstInterfaceMethodRef(type, nat);
            break;
          }
        case CONSTANT_NameAndType:
          {
            int nameIndex = bytes.getUnsignedShort(at + 1);
            CstString name = (CstString) parse0(nameIndex, wasUtf8);
            int descriptorIndex = bytes.getUnsignedShort(at + 3);
            CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
            cst = new CstNat(name, descriptor);
            break;
          }
        case CONSTANT_MethodHandle:
          {
            final int kind = bytes.getUnsignedByte(at + 1);
            final int constantIndex = bytes.getUnsignedShort(at + 2);
            final Constant ref;
            switch (kind) {
              case MethodHandleKind.REF_getField:
              case MethodHandleKind.REF_getStatic:
              case MethodHandleKind.REF_putField:
              case MethodHandleKind.REF_putStatic:
                ref = parse0(constantIndex, wasUtf8);
                break;
              case MethodHandleKind.REF_invokeVirtual:
              case MethodHandleKind.REF_newInvokeSpecial:
                ref = parse0(constantIndex, wasUtf8);
                break;
              case MethodHandleKind.REF_invokeStatic:
              case MethodHandleKind.REF_invokeSpecial:
                ref = parse0(constantIndex, wasUtf8);
                if (!(ref instanceof CstMethodRef || ref instanceof CstInterfaceMethodRef)) {
                  throw new ParseException(
                      "Unsupported ref constant type for MethodHandle " + ref.getClass());
                }
                break;
              case MethodHandleKind.REF_invokeInterface:
                ref = parse0(constantIndex, wasUtf8);
                break;
              default:
                throw new ParseException("Unsupported MethodHandle kind: " + kind);
            }

            final int methodHandleType = getMethodHandleTypeForKind(kind);
            cst = CstMethodHandle.make(methodHandleType, ref);
            break;
          }
        case CONSTANT_MethodType:
          {
            int descriptorIndex = bytes.getUnsignedShort(at + 1);
            CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
            cst = CstProtoRef.make(descriptor);
            break;
          }
        case CONSTANT_InvokeDynamic:
          {
            int bootstrapMethodIndex = bytes.getUnsignedShort(at + 1);
            int natIndex = bytes.getUnsignedShort(at + 3);
            CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
            cst = CstInvokeDynamic.make(bootstrapMethodIndex, nat);
            break;
          }
        default:
          {
            throw new ParseException("unknown tag byte: " + Hex.u1(tag));
          }
      }
    } catch (ParseException ex) {
      ex.addContext("...while parsing cst " + Hex.u2(idx) + " at offset " + Hex.u4(at));
      throw ex;
    } catch (RuntimeException ex) {
      ParseException pe = new ParseException(ex);
      pe.addContext("...while parsing cst " + Hex.u2(idx) + " at offset " + Hex.u4(at));
      throw pe;
    }

    pool.set(idx, cst);
    return cst;
  }
Parses the constant for the given index if it hasn't already been parsed, also storing it in
the constant pool. This will also have the side effect of parsing any entries the indicated one
depends on.

@param idx which constant
@return {@code non-null;} the parsed constant
android/android_framework/dex-maker/src/main/java/com/android/dx/cf/cst/ConstantPoolParser.java