content: package com.alibaba.fastjson2.internal.asm;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  /**
   * Created by wenshao on 05/08/2017.
   */
  public class ClassReader {
      public final byte[] b;
      private final int[] items;
      private final String[] strings;
      private final int maxStringLength;
      public final int header;
  //    private boolean readAnnotations;
  
      public ClassReader(InputStream is) throws IOException {
  //        this.readAnnotations = readAnnotations;
  
          {
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              byte[] buf = new byte[1024];
              for (; ; ) {
                  int len = is.read(buf);
                  if (len == -1) {
                      break;
                  }
  
                  if (len > 0) {
                      out.write(buf, 0, len);
                  }
              }
              is.close();
              this.b = out.toByteArray();
          }
  
          // parses the constant pool
          items = new int[readUnsignedShort(8)];
          int n = items.length;
          strings = new String[n];
          int max = 0;
          int index = 10;
          for (int i = 1; i < n; ++i) {
              items[i] = index + 1;
              int size;
              switch (b[index]) {
                  case 9: // FIELD:
                  case 10: // METH:
                  case 11: //IMETH:
                  case 3: //INT:
                  case 4: //FLOAT:
                  case 18: //INVOKEDYN:
                  case 12: //NAME_TYPE:
                      size = 5;
                      break;
                  case 5: //LONG:
                  case 6: //DOUBLE:
                      size = 9;
                      ++i;
                      break;
                  case 15: //MHANDLE:
                      size = 4;
                      break;
                  case 1: //UTF8:
                      size = 3 + readUnsignedShort(index + 1);
                      if (size > max) {
                          max = size;
                      }
                      break;
                  // case HamConstants.CLASS:
                  // case HamConstants.STR:
                  default:
                      size = 3;
                      break;
              }
              index += size;
          }
          maxStringLength = max;
          // the class header information starts just after the constant pool
          header = index;
      }
  
      public void accept(final TypeCollector classVisitor) {
          char[] c = new char[maxStringLength]; // buffer used to read strings
          int i, j; // loop variables
          int u, v; // indexes in b
  
          //read annotations
  //        if (readAnnotations) {
  //            u = getAttributes();
  //            for (i = readUnsignedShort(u); i > 0; --i) {
  //                String attrName = readUTF8(u + 2, c);
  //                if ("RuntimeVisibleAnnotations".equals(attrName)) {
  //                    anns = u + 8;
  //                    break;
  //                }
  //                u += 6 + readInt(u + 4);
  //            }
  //        }
  
          // visits the header
          u = header;
          int len = readUnsignedShort(u + 6);
          u += 8;
          for (i = 0; i < len; ++i) {
              u += 2;
          }
          v = u;
          i = readUnsignedShort(v);
          v += 2;
          for (; i > 0; --i) {
              j = readUnsignedShort(v + 6);
              v += 8;
              for (; j > 0; --j) {
                  v += 6 + readInt(v + 2);
              }
          }
          i = readUnsignedShort(v);
          v += 2;
          for (; i > 0; --i) {
              j = readUnsignedShort(v + 6);
              v += 8;
              for (; j > 0; --j) {
                  v += 6 + readInt(v + 2);
              }
          }
  
          i = readUnsignedShort(v);
          v += 2;
          for (; i > 0; --i) {
              v += 6 + readInt(v + 2);
          }
  
          // visits the fields
          i = readUnsignedShort(u);
          u += 2;
          for (; i > 0; --i) {
              j = readUnsignedShort(u + 6);
              u += 8;
              for (; j > 0; --j) {
                  u += 6 + readInt(u + 2);
              }
          }
  
          // visits the methods
          i = readUnsignedShort(u);
          u += 2;
          for (; i > 0; --i) {
              // inlined in original ASM source, now a method call
              u = readMethod(classVisitor, c, u);
          }
      }
  
      private int readMethod(TypeCollector classVisitor, char[] c, int u) {
          int v;
          int w;
          int j;
          String attrName;
          int k;
          int access = readUnsignedShort(u);
          String name = readUTF8(u + 2, c);
          String desc = readUTF8(u + 4, c);
          v = 0;
  
          // looks for Code and Exceptions attributes
          j = readUnsignedShort(u + 6);
          u += 8;
          for (; j > 0; --j) {
              attrName = readUTF8(u, c);
              int attrSize = readInt(u + 2);
              u += 6;
              // tests are sorted in decreasing frequency order
              // (based on frequencies observed on typical classes)
              if ("Code".equals(attrName)) {
                  v = u;
              }
              u += attrSize;
          }
          // reads declared exceptions
  
          // visits the method's code, if any
          MethodCollector mv = classVisitor.visitMethod(access, name, desc);
  
          if (mv != null && v != 0) {
              int codeLength = readInt(v + 4);
              v += 8;
  
              v = v + codeLength;
  
              j = readUnsignedShort(v);
              v += 2;
              for (; j > 0; --j) {
                  v += 8;
              }
              // parses the local variable, line number tables, and code
              // attributes
              int varTable = 0;
              int varTypeTable = 0;
              j = readUnsignedShort(v);
              v += 2;
              for (; j > 0; --j) {
                  attrName = readUTF8(v, c);
                  if ("LocalVariableTable".equals(attrName)) {
                      varTable = v + 6;
                  } else if ("LocalVariableTypeTable".equals(attrName)) {
                      varTypeTable = v + 6;
                  }
                  v += 6 + readInt(v + 2);
              }
  
              // visits the local variable tables
              if (varTable != 0) {
                  if (varTypeTable != 0) {
                      k = readUnsignedShort(varTypeTable) * 3;
                      w = varTypeTable + 2;
                      while (k > 0) {
                          k -= 3; // signature + index + start
                          w += 10;
                      }
                  }
                  k = readUnsignedShort(varTable);
                  w = varTable + 2;
                  for (; k > 0; --k) {
                      int index = readUnsignedShort(w + 8);
                      mv.visitLocalVariable(readUTF8(w + 4, c), index);
                      w += 10;
                  }
              }
          }
          return u;
      }
  
      private int readUnsignedShort(final int index) {
          byte[] b = this.b;
          return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
      }
  
      private int readInt(final int index) {
          byte[] b = this.b;
          return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
                  | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
      }
  
      private String readUTF8(int index, final char[] buf) {
          int item = readUnsignedShort(index);
          String s = strings[item];
          if (s != null) {
              return s;
          }
          index = items[item];
          return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
      }
  
      private String readUTF(int index, final int utfLen, final char[] buf) {
          int endIndex = index + utfLen;
          byte[] b = this.b;
          int strLen = 0;
          int c;
          int st = 0;
          char cc = 0;
          while (index < endIndex) {
              c = b[index++];
              switch (st) {
                  case 0:
                      c = c & 0xFF;
                      if (c < 0x80) {  // 0xxxxxxx
                          buf[strLen++] = (char) c;
                      } else if (c < 0xE0 && c > 0xBF) {  // 110x xxxx 10xx xxxx
                          cc = (char) (c & 0x1F);
                          st = 1;
                      } else {  // 1110 xxxx 10xx xxxx 10xx xxxx
                          cc = (char) (c & 0x0F);
                          st = 2;
                      }
                      break;
  
                  case 1:  // byte 2 of 2-byte char or byte 3 of 3-byte char
                      buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
                      st = 0;
                      break;
  
                  case 2:  // byte 2 of 3-byte char
                      cc = (char) ((cc << 6) | (c & 0x3F));
                      st = 1;
                      break;
              }
          }
          return new String(buf, 0, strLen);
      }
  }
  