langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/Scan.java
changeset 41863 98b049c8b848
parent 40596 43b19b36e8e6
child 44877 1ab95a256567
equal deleted inserted replaced
41862:471a0cb1b986 41863:98b049c8b848
    26 package com.sun.tools.jdeprscan.scan;
    26 package com.sun.tools.jdeprscan.scan;
    27 
    27 
    28 import java.io.IOException;
    28 import java.io.IOException;
    29 import java.io.PrintStream;
    29 import java.io.PrintStream;
    30 import java.nio.file.Files;
    30 import java.nio.file.Files;
       
    31 import java.nio.file.NoSuchFileException;
    31 import java.nio.file.Path;
    32 import java.nio.file.Path;
    32 import java.nio.file.Paths;
    33 import java.nio.file.Paths;
    33 import java.util.ArrayDeque;
    34 import java.util.ArrayDeque;
    34 import java.util.Deque;
    35 import java.util.Deque;
    35 import java.util.Enumeration;
    36 import java.util.Enumeration;
    60     final List<String> classPath;
    61     final List<String> classPath;
    61     final DeprDB db;
    62     final DeprDB db;
    62     final boolean verbose;
    63     final boolean verbose;
    63 
    64 
    64     final ClassFinder finder;
    65     final ClassFinder finder;
    65     boolean error = false;
    66     boolean errorOccurred = false;
    66 
    67 
    67     public Scan(PrintStream out,
    68     public Scan(PrintStream out,
    68                 PrintStream err,
    69                 PrintStream err,
    69                 List<String> classPath,
    70                 List<String> classPath,
    70                 DeprDB db,
    71                 DeprDB db,
   122         } else {
   123         } else {
   123             return "class";
   124             return "class";
   124         }
   125         }
   125     }
   126     }
   126 
   127 
   127     void printType(String key, ClassFile cf, String cname, boolean forRemoval)
   128     String dep(boolean forRemoval) {
   128             throws ConstantPoolException {
   129         return Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   129         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   130     }
   130         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep));
   131 
       
   132     void printType(String key, ClassFile cf, String cname, boolean r)
       
   133             throws ConstantPoolException {
       
   134         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep(r)));
   131     }
   135     }
   132 
   136 
   133     void printMethod(String key, ClassFile cf, String cname, String mname, String rtype,
   137     void printMethod(String key, ClassFile cf, String cname, String mname, String rtype,
   134                      boolean forRemoval) throws ConstantPoolException {
   138                      boolean r) throws ConstantPoolException {
   135         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   139         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep(r)));
   136         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep));
       
   137     }
   140     }
   138 
   141 
   139     void printField(String key, ClassFile cf, String cname, String fname,
   142     void printField(String key, ClassFile cf, String cname, String fname,
   140                      boolean forRemoval) throws ConstantPoolException {
   143                      boolean r) throws ConstantPoolException {
   141         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   144         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep(r)));
   142         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep));
       
   143     }
   145     }
   144 
   146 
   145     void printFieldType(String key, ClassFile cf, String cname, String fname, String type,
   147     void printFieldType(String key, ClassFile cf, String cname, String fname, String type,
   146                      boolean forRemoval) throws ConstantPoolException {
   148                      boolean r) throws ConstantPoolException {
   147         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   149         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep(r)));
   148         out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep));
   150     }
   149     }
   151 
   150 
   152     void printHasField(ClassFile cf, String fname, String type, boolean r)
   151     void printHasField(ClassFile cf, String fname, String type, boolean forRemoval)
   153             throws ConstantPoolException {
   152             throws ConstantPoolException {
   154         out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep(r)));
   153         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   155     }
   154         out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep));
   156 
   155     }
   157     void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean r)
   156 
   158             throws ConstantPoolException {
   157     void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean forRemoval)
   159         out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep(r)));
   158             throws ConstantPoolException {
   160     }
   159         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   161 
   160         out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep));
   162     void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean r)
   161     }
   163             throws ConstantPoolException {
   162 
   164         out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep(r)));
   163     void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean forRemoval)
   165     }
   164             throws ConstantPoolException {
   166 
   165         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
   167     void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean r)
   166         out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep));
   168             throws ConstantPoolException {
   167     }
       
   168 
       
   169     void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean forRemoval)
       
   170             throws ConstantPoolException {
       
   171         String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
       
   172         out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden,
   169         out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden,
   173                                  mname, desc, dep));
   170                                  mname, desc, dep(r)));
   174     }
   171     }
   175 
   172 
   176     // format should not have a newline
   173     void errorException(Exception ex) {
   177     void err(String format, Object... args) {
   174         errorOccurred = true;
   178         error = true;
   175         err.println(Messages.get("scan.err.exception", ex.toString()));
   179         err.print("error: ");
       
   180         err.printf(format, args);
       
   181         err.println();
       
   182     }
       
   183 
       
   184     void printException(Exception ex) {
       
   185         err.print(Messages.get("error.prefix"));
       
   186         err.print(" ");
       
   187         if (verbose) {
   176         if (verbose) {
   188             ex.printStackTrace(err);
   177             ex.printStackTrace(err);
   189         } else {
   178         }
   190             err.print(ex);
   179     }
   191         }
   180 
       
   181     void errorNoClass(String className) {
       
   182         errorOccurred = true;
       
   183         err.println(Messages.get("scan.err.noclass", className));
       
   184     }
       
   185 
       
   186     void errorNoFile(String fileName) {
       
   187         errorOccurred = true;
       
   188         err.println(Messages.get("scan.err.nofile", fileName));
       
   189     }
       
   190 
       
   191     void errorNoMethod(String className, String methodName, String desc) {
       
   192         errorOccurred = true;
       
   193         err.println(Messages.get("scan.err.nomethod", className, methodName, desc));
   192     }
   194     }
   193 
   195 
   194     /**
   196     /**
   195      * Checks whether a member (method or field) is present in a class.
   197      * Checks whether a member (method or field) is present in a class.
   196      * The checkMethod parameter determines whether this checks for a method
   198      * The checkMethod parameter determines whether this checks for a method
   269         if (cf.getName().equals(startClassName)) {
   271         if (cf.getName().equals(startClassName)) {
   270             startClass = cf;
   272             startClass = cf;
   271         } else {
   273         } else {
   272             startClass = finder.find(startClassName);
   274             startClass = finder.find(startClassName);
   273             if (startClass == null) {
   275             if (startClass == null) {
   274                 err("can't find class %s", startClassName);
   276                 errorNoClass(startClassName);
   275                 return startClassName;
   277                 return startClassName;
   276             }
   278             }
   277         }
   279         }
   278 
   280 
   279         // follow super_class until it's 0, meaning we've reached Object
   281         // follow super_class until it's 0, meaning we've reached Object
   293             }
   295             }
   294 
   296 
   295             String superName = curClass.getSuperclassName();
   297             String superName = curClass.getSuperclassName();
   296             curClass = finder.find(superName);
   298             curClass = finder.find(superName);
   297             if (curClass == null) {
   299             if (curClass == null) {
   298                 err("can't find class %s", superName);
   300                 errorNoClass(superName);
   299                 break;
   301                 break;
   300             }
   302             }
   301             addInterfaces(intfs, curClass);
   303             addInterfaces(intfs, curClass);
   302         }
   304         }
   303 
   305 
   308             addInterfaces(intfs, startClass);
   310             addInterfaces(intfs, startClass);
   309             while (intfs.size() > 0) {
   311             while (intfs.size() > 0) {
   310                 String intf = intfs.removeFirst();
   312                 String intf = intfs.removeFirst();
   311                 curClass = finder.find(intf);
   313                 curClass = finder.find(intf);
   312                 if (curClass == null) {
   314                 if (curClass == null) {
   313                     err("can't find interface %s", intf);
   315                     errorNoClass(intf);
   314                     break;
   316                     break;
   315                 }
   317                 }
   316 
   318 
   317                 if (isMemberPresent(curClass, findName, findDesc, resolveMethod)) {
   319                 if (isMemberPresent(curClass, findName, findDesc, resolveMethod)) {
   318                     break;
   320                     break;
   322             }
   324             }
   323         }
   325         }
   324 
   326 
   325         if (curClass == null) {
   327         if (curClass == null) {
   326             if (checkStartClass) {
   328             if (checkStartClass) {
   327                 err("can't resolve methodref %s %s %s",
   329                 errorNoMethod(startClassName, findName, findDesc);
   328                     startClassName, findName, findDesc);
       
   329                 return startClassName;
   330                 return startClassName;
   330             } else {
   331             } else {
   331                 // TODO: refactor this
   332                 // TODO: refactor this
   332                 // checkStartClass == false means we're checking for overrides
   333                 // checkStartClass == false means we're checking for overrides
   333                 // so not being able to resolve a method simply means there's
   334                 // so not being able to resolve a method simply means there's
   370             }
   371             }
   371         }
   372         }
   372     }
   373     }
   373 
   374 
   374     /**
   375     /**
   375      * Checks types referred to from the constant pool.
   376      * Checks Class_info entries in the constant pool.
   376      *
   377      *
   377      * @param cf the ClassFile of this class
   378      * @param cf the ClassFile of this class
   378      * @param entries constant pool entries collected from this class
   379      * @param entries constant pool entries collected from this class
   379      * @throws ConstantPoolException if a constant pool entry cannot be found
   380      * @throws ConstantPoolException if a constant pool entry cannot be found
   380      */
   381      */
   381     void checkTypes(ClassFile cf, CPEntries entries) throws ConstantPoolException {
   382     void checkClasses(ClassFile cf, CPEntries entries) throws ConstantPoolException {
   382         for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
   383         for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
   383             String typeName = ci.getName();
   384             String className = ci.getName();
   384             DeprData dd = db.getTypeDeprecated(flatten(typeName));
   385             DeprData dd = db.getTypeDeprecated(flatten(className));
   385             if (dd != null) {
   386             if (dd != null) {
   386                 printType("scan.out.usestype", cf, typeName, dd.isForRemoval());
   387                 printType("scan.out.usesclass", cf, className, dd.isForRemoval());
   387             }
   388             }
   388         }
   389         }
   389     }
   390     }
   390 
   391 
   391     /**
   392     /**
   392      * Checks methods referred to from the constant pool.
   393      * Checks methods referred to from the constant pool.
   393      *
   394      *
   394      * @param cf the ClassFile of this class
   395      * @param cf the ClassFile of this class
   395      * @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
   396      * @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
   396      * @param clname the class name
   397      * @param clname the class name
   397      * @param typeKey key for the type message
   398      * @param msgKey message key for localization
   398      * @param methKey key for the method message
       
   399      * @throws ConstantPoolException if a constant pool entry cannot be found
   399      * @throws ConstantPoolException if a constant pool entry cannot be found
   400      */
   400      */
   401     void checkMethodRef(ClassFile cf,
   401     void checkMethodRef(ClassFile cf,
       
   402                         String clname,
   402                         CONSTANT_NameAndType_info nti,
   403                         CONSTANT_NameAndType_info nti,
   403                         String clname,
   404                         String msgKey) throws ConstantPoolException {
   404                         String typeKey,
       
   405                         String methKey) throws ConstantPoolException {
       
   406         DeprData dd = db.getTypeDeprecated(flatten(clname));
       
   407         if (dd != null) {
       
   408             printType(typeKey, cf, clname, dd.isForRemoval());
       
   409         }
       
   410 
       
   411         String name = nti.getName();
   405         String name = nti.getName();
   412         String type = nti.getType();
   406         String type = nti.getType();
   413         clname = resolveMember(cf, flatten(clname), name, type, true, true);
   407         clname = resolveMember(cf, flatten(clname), name, type, true, true);
   414         dd = db.getMethodDeprecated(clname, name, type);
   408         DeprData dd = db.getMethodDeprecated(clname, name, type);
   415         if (dd != null) {
   409         if (dd != null) {
   416             printMethod(methKey, cf, clname, name, type, dd.isForRemoval());
   410             printMethod(msgKey, cf, clname, name, type, dd.isForRemoval());
   417         }
   411         }
   418     }
   412     }
   419 
   413 
   420     /**
   414     /**
   421      * Checks fields referred to from the constant pool.
   415      * Checks fields referred to from the constant pool.
   423      * @param cf the ClassFile of this class
   417      * @param cf the ClassFile of this class
   424      * @throws ConstantPoolException if a constant pool entry cannot be found
   418      * @throws ConstantPoolException if a constant pool entry cannot be found
   425      */
   419      */
   426     void checkFieldRef(ClassFile cf,
   420     void checkFieldRef(ClassFile cf,
   427                        ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
   421                        ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
       
   422         String clname = fri.getClassName();
   428         CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
   423         CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
   429         String clname = fri.getClassName();
       
   430         String name = nti.getName();
   424         String name = nti.getName();
   431         String type = nti.getType();
   425         String type = nti.getType();
   432         DeprData dd = db.getTypeDeprecated(clname);
       
   433 
       
   434         if (dd != null) {
       
   435             printType("scan.out.usesfieldintype", cf, clname, dd.isForRemoval());
       
   436         }
       
   437 
   426 
   438         clname = resolveMember(cf, flatten(clname), name, type, false, true);
   427         clname = resolveMember(cf, flatten(clname), name, type, false, true);
   439         dd = db.getFieldDeprecated(clname, name);
   428         DeprData dd = db.getFieldDeprecated(clname, name);
   440         if (dd != null) {
   429         if (dd != null) {
   441             printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
   430             printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
   442         }
       
   443 
       
   444         dd = db.getTypeDeprecated(flatten(type));
       
   445         if (dd != null) {
       
   446             printFieldType("scan.out.usesfieldoftype", cf, clname, name, type, dd.isForRemoval());
       
   447         }
   431         }
   448     }
   432     }
   449 
   433 
   450     /**
   434     /**
   451      * Checks the fields declared in this class.
   435      * Checks the fields declared in this class.
   513 
   497 
   514         CPEntries entries = CPEntries.loadFrom(cf);
   498         CPEntries entries = CPEntries.loadFrom(cf);
   515 
   499 
   516         checkSuper(cf);
   500         checkSuper(cf);
   517         checkInterfaces(cf);
   501         checkInterfaces(cf);
   518         checkTypes(cf, entries);
   502         checkClasses(cf, entries);
   519 
   503 
   520         for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) {
   504         for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) {
       
   505             String clname = mri.getClassName();
   521             CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
   506             CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
   522             String clname = mri.getClassName();
   507             checkMethodRef(cf, clname, nti, "scan.out.usesmethod");
   523             checkMethodRef(cf, nti, clname, "scan.out.usesmethodintype", "scan.out.usesmethod");
       
   524         }
   508         }
   525 
   509 
   526         for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) {
   510         for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) {
       
   511             String clname = imri.getClassName();
   527             CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
   512             CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
   528             String clname = imri.getClassName();
   513             checkMethodRef(cf, clname, nti, "scan.out.usesintfmethod");
   529             checkMethodRef(cf, nti, clname, "scan.out.usesintfmethodintype", "scan.out.usesintfmethod");
       
   530         }
   514         }
   531 
   515 
   532         for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) {
   516         for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) {
   533             checkFieldRef(cf, fri);
   517             checkFieldRef(cf, fri);
   534         }
   518         }
   543      * @param jarname the jar file to process
   527      * @param jarname the jar file to process
   544      * @return true on success, false on failure
   528      * @return true on success, false on failure
   545      */
   529      */
   546     public boolean scanJar(String jarname) {
   530     public boolean scanJar(String jarname) {
   547         try (JarFile jf = new JarFile(jarname)) {
   531         try (JarFile jf = new JarFile(jarname)) {
       
   532             out.println(Messages.get("scan.head.jar", jarname));
   548             finder.addJar(jarname);
   533             finder.addJar(jarname);
   549             Enumeration<JarEntry> entries = jf.entries();
   534             Enumeration<JarEntry> entries = jf.entries();
   550             while (entries.hasMoreElements()) {
   535             while (entries.hasMoreElements()) {
   551                 JarEntry entry = entries.nextElement();
   536                 JarEntry entry = entries.nextElement();
   552                 String name = entry.getName();
   537                 String name = entry.getName();
   555                         && !name.endsWith("module-info.class")) {
   540                         && !name.endsWith("module-info.class")) {
   556                     processClass(ClassFile.read(jf.getInputStream(entry)));
   541                     processClass(ClassFile.read(jf.getInputStream(entry)));
   557                 }
   542                 }
   558             }
   543             }
   559             return true;
   544             return true;
       
   545         } catch (NoSuchFileException nsfe) {
       
   546             errorNoFile(jarname);
   560         } catch (IOException | ConstantPoolException ex) {
   547         } catch (IOException | ConstantPoolException ex) {
   561             printException(ex);
   548             errorException(ex);
   562             return false;
   549         }
   563         }
   550         return false;
   564     }
   551     }
   565 
   552 
   566     /**
   553     /**
   567      * Scans class files in the named directory hierarchy for uses of deprecated APIs.
   554      * Scans class files in the named directory hierarchy for uses of deprecated APIs.
   568      *
   555      *
   578                 paths.filter(p -> p.getNameCount() > baseCount)
   565                 paths.filter(p -> p.getNameCount() > baseCount)
   579                      .filter(path -> path.toString().endsWith(".class"))
   566                      .filter(path -> path.toString().endsWith(".class"))
   580                      .filter(path -> !path.toString().endsWith("package-info.class"))
   567                      .filter(path -> !path.toString().endsWith("package-info.class"))
   581                      .filter(path -> !path.toString().endsWith("module-info.class"))
   568                      .filter(path -> !path.toString().endsWith("module-info.class"))
   582                      .collect(Collectors.toList());
   569                      .collect(Collectors.toList());
       
   570 
       
   571             out.println(Messages.get("scan.head.dir", dirname));
       
   572 
   583             for (Path p : classes) {
   573             for (Path p : classes) {
   584                 processClass(ClassFile.read(p));
   574                 processClass(ClassFile.read(p));
   585             }
   575             }
   586             return true;
   576             return true;
   587         } catch (IOException | ConstantPoolException ex) {
   577         } catch (IOException | ConstantPoolException ex) {
   588             printException(ex);
   578             errorException(ex);
   589             return false;
   579             return false;
   590         }
   580         }
   591     }
   581     }
   592 
   582 
   593     /**
   583     /**
   598      */
   588      */
   599     public boolean processClassName(String className) {
   589     public boolean processClassName(String className) {
   600         try {
   590         try {
   601             ClassFile cf = finder.find(className);
   591             ClassFile cf = finder.find(className);
   602             if (cf == null) {
   592             if (cf == null) {
   603                 err("can't find class %s", className);
   593                 errorNoClass(className);
   604                 return false;
   594                 return false;
   605             } else {
   595             } else {
   606                 processClass(cf);
   596                 processClass(cf);
   607                 return true;
   597                 return true;
   608             }
   598             }
   609         } catch (ConstantPoolException ex) {
   599         } catch (ConstantPoolException ex) {
   610             printException(ex);
   600             errorException(ex);
   611             return false;
   601             return false;
   612         }
   602         }
   613     }
   603     }
       
   604 
       
   605     /**
       
   606      * Scans the named class file for uses of deprecated APIs.
       
   607      *
       
   608      * @param fileName the class file to scan
       
   609      * @return true on success, false on failure
       
   610      */
       
   611     public boolean processClassFile(String fileName) {
       
   612         Path path = Paths.get(fileName);
       
   613         try {
       
   614             ClassFile cf = ClassFile.read(path);
       
   615             processClass(cf);
       
   616             return true;
       
   617         } catch (NoSuchFileException nsfe) {
       
   618             errorNoFile(fileName);
       
   619         } catch (IOException | ConstantPoolException ex) {
       
   620             errorException(ex);
       
   621         }
       
   622         return false;
       
   623     }
   614 }
   624 }