code: /*
   * Copyright (c) 2008-2019 Emmanuel Dupuy.
   * This project is distributed under the GPLv3 license.
   * This is a Copyleft license that gives the user the right to use,
   * copy and modify the code freely for non-commercial purposes.
   */
  
  package org.jd.gui.service.indexer;
  
  import org.jd.gui.api.API;
  import org.jd.gui.api.model.Container;
  import org.jd.gui.api.model.Indexes;
  import org.jd.gui.util.exception.ExceptionUtil;
  import org.objectweb.asm.*;
  import org.objectweb.asm.signature.SignatureReader;
  import org.objectweb.asm.signature.SignatureVisitor;
  
  import java.io.InputStream;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.regex.Pattern;
  
  import static org.objectweb.asm.ClassReader.*;
  
  /**
   * Unsafe thread implementation of class file indexer.
   */
  public class ClassFileIndexerProvider extends AbstractIndexerProvider {
      protected HashSet<String> typeDeclarationSet = new HashSet<>();
      protected HashSet<String> constructorDeclarationSet = new HashSet<>();
      protected HashSet<String> methodDeclarationSet = new HashSet<>();
      protected HashSet<String> fieldDeclarationSet = new HashSet<>();
      protected HashSet<String> typeReferenceSet = new HashSet<>();
      protected HashSet<String> constructorReferenceSet = new HashSet<>();
      protected HashSet<String> methodReferenceSet = new HashSet<>();
      protected HashSet<String> fieldReferenceSet = new HashSet<>();
      protected HashSet<String> stringSet = new HashSet<>();
      protected HashSet<String> superTypeNameSet = new HashSet<>();
      protected HashSet<String> descriptorSet = new HashSet<>();
  
      protected ClassIndexer classIndexer = new ClassIndexer();
      protected SignatureIndexer signatureIndexer = new SignatureIndexer();
  
      @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); }
  
      @Override
      public Pattern getPathPattern() {
          if (externalPathPattern == null) {
              return Pattern.compile("^((?!module-info\\.class).)*$");
          } else {
              return externalPathPattern;
          }
      }
  
      @Override
      @SuppressWarnings("unchecked")
      public void index(API api, Container.Entry entry, Indexes indexes) {
          // Cleaning sets...
          typeDeclarationSet.clear();
          constructorDeclarationSet.clear();
          methodDeclarationSet.clear();
          fieldDeclarationSet.clear();
          typeReferenceSet.clear();
          constructorReferenceSet.clear();
          methodReferenceSet.clear();
          fieldReferenceSet.clear();
          stringSet.clear();
          superTypeNameSet.clear();
          descriptorSet.clear();
  
          try (InputStream inputStream = entry.getInputStream()) {
              // Index field, method, interfaces & super type
              ClassReader classReader = new ClassReader(inputStream);
              classReader.accept(classIndexer, SKIP_CODE|SKIP_DEBUG|SKIP_FRAMES);
  
              // Index descriptors
              for (String descriptor : descriptorSet) {
                  new SignatureReader(descriptor).accept(signatureIndexer);
              }
  
              // Index references
              char[] buffer = new char[classReader.getMaxStringLength()];
  
              for (int i=classReader.getItemCount()-1; i>0; i--) {
                  int startIndex = classReader.getItem(i);
  
                  if (startIndex != 0) {
                      int tag = classReader.readByte(startIndex-1);
  
                      switch (tag) {
                          case 7: // CONSTANT_Class
                              String className = classReader.readUTF8(startIndex, buffer);
                              if (className.startsWith("[")) {
                                  new SignatureReader(className).acceptType(signatureIndexer);
                              } else {
                                  typeReferenceSet.add(className);
                              }
                              break;
                          case 8: // CONSTANT_String
                              String str = classReader.readUTF8(startIndex, buffer);
                              stringSet.add(str);
                              break;
                          case 9: // CONSTANT_Fieldref
                              int nameAndTypeItem = classReader.readUnsignedShort(startIndex+2);
                              int nameAndTypeIndex = classReader.getItem(nameAndTypeItem);
                              tag = classReader.readByte(nameAndTypeIndex-1);
                              if (tag == 12) { // CONSTANT_NameAndType
                                  String fieldName = classReader.readUTF8(nameAndTypeIndex, buffer);
                                  fieldReferenceSet.add(fieldName);
                              }
                              break;
                          case 10: // CONSTANT_Methodref:
                          case 11: // CONSTANT_InterfaceMethodref:
                              nameAndTypeItem = classReader.readUnsignedShort(startIndex+2);
                              nameAndTypeIndex = classReader.getItem(nameAndTypeItem);
                              tag = classReader.readByte(nameAndTypeIndex-1);
                              if (tag == 12) { // CONSTANT_NameAndType
                                  String methodName = classReader.readUTF8(nameAndTypeIndex, buffer);
                                  if ("<init>".equals(methodName)) {
                                      int classItem = classReader.readUnsignedShort(startIndex);
                                      int classIndex = classReader.getItem(classItem);
                                      className = classReader.readUTF8(classIndex, buffer);
                                      constructorReferenceSet.add(className);
                                  } else {
                                      methodReferenceSet.add(methodName);
                                  }
                              }
                              break;
                      }
                  }
              }
  
              String typeName = classIndexer.name;
  
              // Append sets to indexes
              addToIndexes(indexes, "typeDeclarations", typeDeclarationSet, entry);
              addToIndexes(indexes, "constructorDeclarations", constructorDeclarationSet, entry);
              addToIndexes(indexes, "methodDeclarations", methodDeclarationSet, entry);
              addToIndexes(indexes, "fieldDeclarations", fieldDeclarationSet, entry);
              addToIndexes(indexes, "typeReferences", typeReferenceSet, entry);
              addToIndexes(indexes, "constructorReferences", constructorReferenceSet, entry);
              addToIndexes(indexes, "methodReferences", methodReferenceSet, entry);
              addToIndexes(indexes, "fieldReferences", fieldReferenceSet, entry);
              addToIndexes(indexes, "strings", stringSet, entry);
  
              // Populate map [super type name : [sub type name]]
              if (superTypeNameSet.size() > 0) {
                  Map<String, Collection> index = indexes.getIndex("subTypeNames");
  
                  for (String superTypeName : superTypeNameSet) {
                      index.get(superTypeName).add(typeName);
                  }
              }
          } catch (Exception e) {
              assert ExceptionUtil.printStackTrace(e);
          }
      }
  
      protected class ClassIndexer extends ClassVisitor {
          protected AnnotationIndexer annotationIndexer = new AnnotationIndexer();
          protected FieldIndexer fieldIndexer = new FieldIndexer(annotationIndexer);
          protected MethodIndexer methodIndexer = new MethodIndexer(annotationIndexer);
  
          protected String name;
  
          public ClassIndexer() { super(Opcodes.ASM7); }
  
          @Override
          public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
              this.name = name;
              typeDeclarationSet.add(name);
  
              if (superName != null) {
                  superTypeNameSet.add(superName);
              }
  
              if (interfaces != null) {
                  for (int i=interfaces.length-1; i>=0; i--) {
                      superTypeNameSet.add(interfaces[i]);
                  }
              }
          }
  
          @Override
          public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
  
          @Override
          public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
              fieldDeclarationSet.add(name);
              descriptorSet.add(signature==null ? desc : signature);
              return fieldIndexer;
          }
  
          @Override
          public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
              if ("<init>".equals(name)) {
                  constructorDeclarationSet.add(this.name);
              } else if (! "<clinit>".equals(name)) {
                  methodDeclarationSet.add(name);
              }
  
              descriptorSet.add(signature==null ? desc : signature);
  
              if (exceptions != null) {
                  for (int i=exceptions.length-1; i>=0; i--) {
                      typeReferenceSet.add(exceptions[i]);
                  }
              }
              return methodIndexer;
          }
      }
  
      protected class SignatureIndexer extends SignatureVisitor {
          SignatureIndexer() { super(Opcodes.ASM7); }
  
          @Override public void visitClassType(String name) { typeReferenceSet.add(name); }
      }
  
      protected class AnnotationIndexer extends AnnotationVisitor {
          public AnnotationIndexer() { super(Opcodes.ASM7); }
  
          @Override public void visitEnum(String name, String desc, String value) { descriptorSet.add(desc); }
  
          @Override
          public AnnotationVisitor visitAnnotation(String name, String desc) {
              descriptorSet.add(desc);
              return this;
          }
      }
  
      protected class FieldIndexer extends FieldVisitor {
          protected AnnotationIndexer annotationIndexer;
  
          public FieldIndexer(AnnotationIndexer annotationIndexer) {
              super(Opcodes.ASM7);
              this.annotationIndexer = annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
      }
  
      protected class MethodIndexer extends MethodVisitor {
          protected AnnotationIndexer annotationIndexer;
  
          public MethodIndexer(AnnotationIndexer annotationIndexer) {
              super(Opcodes.ASM7);
              this.annotationIndexer = annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
  
          @Override
          public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
              descriptorSet.add(desc);
              return annotationIndexer;
          }
      }
  }
  