diff -r 0419f45c7761 -r 44dfee24cb71 jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java Tue Apr 16 21:39:52 2013 -0700 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.tools.classfile.*; +import com.sun.tools.jdeps.ClassFileReader; +import static com.sun.tools.classfile.ConstantPool.*; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; + +/* + * @test + * @bug 8010117 + * @summary Verify if CallerSensitive methods are annotated with + * sun.reflect.CallerSensitive annotation + * @build CallerSensitiveFinder MethodFinder + * @run main/othervm/timeout=900 -mx600m CallerSensitiveFinder + */ +public class CallerSensitiveFinder extends MethodFinder { + private static int numThreads = 3; + private static boolean verbose = false; + public static void main(String[] args) throws Exception { + List classes = new ArrayList<>(); + String testclasses = System.getProperty("test.classes", "."); + int i = 0; + while (i < args.length) { + String arg = args[i++]; + if (arg.equals("-v")) { + verbose = true; + } else { + Path p = Paths.get(testclasses, arg); + if (!p.toFile().exists()) { + throw new IllegalArgumentException(arg + " does not exist"); + } + classes.add(p); + } + } + if (classes.isEmpty()) { + classes.addAll(PlatformClassPath.getJREClasses()); + } + final String method = "sun/reflect/Reflection.getCallerClass"; + CallerSensitiveFinder csfinder = new CallerSensitiveFinder(method); + + List errors = csfinder.run(classes); + if (!errors.isEmpty()) { + throw new RuntimeException(errors.size() + + " caller-sensitive methods are missing @CallerSensitive annotation"); + } + } + + private final List csMethodsMissingAnnotation = new ArrayList<>(); + public CallerSensitiveFinder(String... methods) { + super(methods); + } + + public List run(List classes) throws IOException, InterruptedException, + ExecutionException, ConstantPoolException + { + ExecutorService pool = Executors.newFixedThreadPool(numThreads); + for (Path path : classes) { + ClassFileReader reader = ClassFileReader.newInstance(path.toFile()); + for (ClassFile cf : reader.getClassFiles()) { + String classFileName = cf.getName(); + // for each ClassFile + // parse constant pool to find matching method refs + // parse each method (caller) + // - visit and find method references matching the given method name + pool.submit(getTask(cf)); + } + } + waitForCompletion(); + pool.shutdown(); + return csMethodsMissingAnnotation; + } + + private static final String CALLER_SENSITIVE_ANNOTATION = "Lsun/reflect/CallerSensitive;"; + private static boolean isCallerSensitive(Method m, ConstantPool cp) + throws ConstantPoolException + { + RuntimeAnnotations_attribute attr = + (RuntimeAnnotations_attribute)m.attributes.get(Attribute.RuntimeVisibleAnnotations); + int index = 0; + if (attr != null) { + for (int i = 0; i < attr.annotations.length; i++) { + Annotation ann = attr.annotations[i]; + String annType = cp.getUTF8Value(ann.type_index); + if (CALLER_SENSITIVE_ANNOTATION.equals(annType)) { + return true; + } + } + } + return false; + } + + public void referenceFound(ClassFile cf, Method m, Set refs) + throws ConstantPoolException + { + String name = String.format("%s#%s %s", cf.getName(), + m.getName(cf.constant_pool), + m.descriptor.getValue(cf.constant_pool)); + if (!CallerSensitiveFinder.isCallerSensitive(m, cf.constant_pool)) { + csMethodsMissingAnnotation.add(name); + System.err.println("Missing @CallerSensitive: " + name); + } else { + if (verbose) { + System.out.format("@CS %s%n", name); + } + } + } + + private final List> tasks = new ArrayList>(); + private FutureTask getTask(final ClassFile cf) { + FutureTask task = new FutureTask(new Callable() { + public String call() throws Exception { + return parse(cf); + } + }); + tasks.add(task); + return task; + } + + private void waitForCompletion() throws InterruptedException, ExecutionException { + for (FutureTask t : tasks) { + String s = t.get(); + } + System.out.println("Parsed " + tasks.size() + " classfiles"); + } + + static class PlatformClassPath { + static List getJREClasses() throws IOException { + List result = new ArrayList(); + Path home = Paths.get(System.getProperty("java.home")); + + if (home.endsWith("jre")) { + // jar files in /jre/lib + // skip /lib + result.addAll(addJarFiles(home.resolve("lib"))); + } else if (home.resolve("lib").toFile().exists()) { + // either a JRE or a jdk build image + File classes = home.resolve("classes").toFile(); + if (classes.exists() && classes.isDirectory()) { + // jdk build outputdir + result.add(classes.toPath()); + } + // add other JAR files + result.addAll(addJarFiles(home.resolve("lib"))); + } else { + throw new RuntimeException("\"" + home + "\" not a JDK home"); + } + return result; + } + + static List addJarFiles(final Path root) throws IOException { + final List result = new ArrayList(); + final Path ext = root.resolve("ext"); + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + if (dir.equals(root) || dir.equals(ext)) { + return FileVisitResult.CONTINUE; + } else { + // skip other cobundled JAR files + return FileVisitResult.SKIP_SUBTREE; + } + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + File f = file.toFile(); + String fn = f.getName(); + // parse alt-rt.jar as well + if (fn.endsWith(".jar") && !fn.equals("jfxrt.jar")) { + result.add(file); + } + return FileVisitResult.CONTINUE; + } + }); + return result; + } + } +}