jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java
changeset 16906 44dfee24cb71
child 17204 d0540971275f
equal deleted inserted replaced
16905:0419f45c7761 16906:44dfee24cb71
       
     1 /*
       
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import com.sun.tools.classfile.*;
       
    25 import com.sun.tools.jdeps.ClassFileReader;
       
    26 import static com.sun.tools.classfile.ConstantPool.*;
       
    27 import java.io.File;
       
    28 import java.io.IOException;
       
    29 import java.nio.file.FileVisitResult;
       
    30 import java.nio.file.Files;
       
    31 import java.nio.file.Path;
       
    32 import java.nio.file.Paths;
       
    33 import java.nio.file.SimpleFileVisitor;
       
    34 import java.nio.file.attribute.BasicFileAttributes;
       
    35 import java.util.ArrayList;
       
    36 import java.util.List;
       
    37 import java.util.Set;
       
    38 import java.util.concurrent.Callable;
       
    39 import java.util.concurrent.ExecutionException;
       
    40 import java.util.concurrent.ExecutorService;
       
    41 import java.util.concurrent.Executors;
       
    42 import java.util.concurrent.FutureTask;
       
    43 
       
    44 /*
       
    45  * @test
       
    46  * @bug 8010117
       
    47  * @summary Verify if CallerSensitive methods are annotated with
       
    48  *          sun.reflect.CallerSensitive annotation
       
    49  * @build CallerSensitiveFinder MethodFinder
       
    50  * @run main/othervm/timeout=900 -mx600m CallerSensitiveFinder
       
    51  */
       
    52 public class CallerSensitiveFinder extends MethodFinder {
       
    53     private static int numThreads = 3;
       
    54     private static boolean verbose = false;
       
    55     public static void main(String[] args) throws Exception {
       
    56         List<Path> classes = new ArrayList<>();
       
    57         String testclasses = System.getProperty("test.classes", ".");
       
    58         int i = 0;
       
    59         while (i < args.length) {
       
    60             String arg = args[i++];
       
    61             if (arg.equals("-v")) {
       
    62                 verbose = true;
       
    63             } else {
       
    64                 Path p = Paths.get(testclasses, arg);
       
    65                 if (!p.toFile().exists()) {
       
    66                     throw new IllegalArgumentException(arg + " does not exist");
       
    67                 }
       
    68                 classes.add(p);
       
    69             }
       
    70         }
       
    71         if (classes.isEmpty()) {
       
    72             classes.addAll(PlatformClassPath.getJREClasses());
       
    73         }
       
    74         final String method = "sun/reflect/Reflection.getCallerClass";
       
    75         CallerSensitiveFinder csfinder = new CallerSensitiveFinder(method);
       
    76 
       
    77         List<String> errors = csfinder.run(classes);
       
    78         if (!errors.isEmpty()) {
       
    79             throw new RuntimeException(errors.size() +
       
    80                     " caller-sensitive methods are missing @CallerSensitive annotation");
       
    81         }
       
    82     }
       
    83 
       
    84     private final List<String> csMethodsMissingAnnotation = new ArrayList<>();
       
    85     public CallerSensitiveFinder(String... methods) {
       
    86         super(methods);
       
    87     }
       
    88 
       
    89     public List<String> run(List<Path> classes) throws IOException, InterruptedException,
       
    90             ExecutionException, ConstantPoolException
       
    91     {
       
    92         ExecutorService pool = Executors.newFixedThreadPool(numThreads);
       
    93         for (Path path : classes) {
       
    94             ClassFileReader reader = ClassFileReader.newInstance(path.toFile());
       
    95             for (ClassFile cf : reader.getClassFiles()) {
       
    96                 String classFileName = cf.getName();
       
    97                 // for each ClassFile
       
    98                 //    parse constant pool to find matching method refs
       
    99                 //      parse each method (caller)
       
   100                 //      - visit and find method references matching the given method name
       
   101                 pool.submit(getTask(cf));
       
   102             }
       
   103         }
       
   104         waitForCompletion();
       
   105         pool.shutdown();
       
   106         return csMethodsMissingAnnotation;
       
   107     }
       
   108 
       
   109     private static final String CALLER_SENSITIVE_ANNOTATION = "Lsun/reflect/CallerSensitive;";
       
   110     private static boolean isCallerSensitive(Method m, ConstantPool cp)
       
   111             throws ConstantPoolException
       
   112     {
       
   113         RuntimeAnnotations_attribute attr =
       
   114             (RuntimeAnnotations_attribute)m.attributes.get(Attribute.RuntimeVisibleAnnotations);
       
   115         int index = 0;
       
   116         if (attr != null) {
       
   117             for (int i = 0; i < attr.annotations.length; i++) {
       
   118                 Annotation ann = attr.annotations[i];
       
   119                 String annType = cp.getUTF8Value(ann.type_index);
       
   120                 if (CALLER_SENSITIVE_ANNOTATION.equals(annType)) {
       
   121                     return true;
       
   122                 }
       
   123             }
       
   124         }
       
   125         return false;
       
   126     }
       
   127 
       
   128     public void referenceFound(ClassFile cf, Method m, Set<Integer> refs)
       
   129             throws ConstantPoolException
       
   130     {
       
   131         String name = String.format("%s#%s %s", cf.getName(),
       
   132                                     m.getName(cf.constant_pool),
       
   133                                     m.descriptor.getValue(cf.constant_pool));
       
   134         if (!CallerSensitiveFinder.isCallerSensitive(m, cf.constant_pool)) {
       
   135             csMethodsMissingAnnotation.add(name);
       
   136             System.err.println("Missing @CallerSensitive: " + name);
       
   137         } else {
       
   138             if (verbose) {
       
   139                 System.out.format("@CS  %s%n", name);
       
   140             }
       
   141         }
       
   142     }
       
   143 
       
   144     private final List<FutureTask<String>> tasks = new ArrayList<FutureTask<String>>();
       
   145     private FutureTask<String> getTask(final ClassFile cf) {
       
   146         FutureTask<String> task = new FutureTask<String>(new Callable<String>() {
       
   147             public String call() throws Exception {
       
   148                 return parse(cf);
       
   149             }
       
   150         });
       
   151         tasks.add(task);
       
   152         return task;
       
   153     }
       
   154 
       
   155     private void waitForCompletion() throws InterruptedException, ExecutionException {
       
   156         for (FutureTask<String> t : tasks) {
       
   157             String s = t.get();
       
   158         }
       
   159         System.out.println("Parsed " + tasks.size() + " classfiles");
       
   160     }
       
   161 
       
   162     static class PlatformClassPath {
       
   163         static List<Path> getJREClasses() throws IOException {
       
   164             List<Path> result = new ArrayList<Path>();
       
   165             Path home = Paths.get(System.getProperty("java.home"));
       
   166 
       
   167             if (home.endsWith("jre")) {
       
   168                 // jar files in <javahome>/jre/lib
       
   169                 // skip <javahome>/lib
       
   170                 result.addAll(addJarFiles(home.resolve("lib")));
       
   171             } else if (home.resolve("lib").toFile().exists()) {
       
   172                 // either a JRE or a jdk build image
       
   173                 File classes = home.resolve("classes").toFile();
       
   174                 if (classes.exists() && classes.isDirectory()) {
       
   175                     // jdk build outputdir
       
   176                     result.add(classes.toPath());
       
   177                 }
       
   178                 // add other JAR files
       
   179                 result.addAll(addJarFiles(home.resolve("lib")));
       
   180             } else {
       
   181                 throw new RuntimeException("\"" + home + "\" not a JDK home");
       
   182             }
       
   183             return result;
       
   184         }
       
   185 
       
   186         static List<Path> addJarFiles(final Path root) throws IOException {
       
   187             final List<Path> result = new ArrayList<Path>();
       
   188             final Path ext = root.resolve("ext");
       
   189             Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
       
   190                 @Override
       
   191                 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
       
   192                         throws IOException {
       
   193                     if (dir.equals(root) || dir.equals(ext)) {
       
   194                         return FileVisitResult.CONTINUE;
       
   195                     } else {
       
   196                         // skip other cobundled JAR files
       
   197                         return FileVisitResult.SKIP_SUBTREE;
       
   198                     }
       
   199                 }
       
   200 
       
   201                 @Override
       
   202                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
       
   203                         throws IOException {
       
   204                     File f = file.toFile();
       
   205                     String fn = f.getName();
       
   206                     // parse alt-rt.jar as well
       
   207                     if (fn.endsWith(".jar") && !fn.equals("jfxrt.jar")) {
       
   208                         result.add(file);
       
   209                     }
       
   210                     return FileVisitResult.CONTINUE;
       
   211                 }
       
   212             });
       
   213             return result;
       
   214         }
       
   215     }
       
   216 }