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