2013-04-16 21:39:52 -07:00
|
|
|
/*
|
|
|
|
* 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
|
2013-04-30 15:42:16 -07:00
|
|
|
* @build CallerSensitiveFinder
|
2013-04-16 21:39:52 -07:00
|
|
|
* @run main/othervm/timeout=900 -mx600m CallerSensitiveFinder
|
|
|
|
*/
|
2013-04-30 15:42:16 -07:00
|
|
|
public class CallerSensitiveFinder {
|
2013-04-16 21:39:52 -07:00
|
|
|
private static int numThreads = 3;
|
|
|
|
private static boolean verbose = false;
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
List<Path> 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());
|
|
|
|
}
|
2013-04-30 15:42:16 -07:00
|
|
|
CallerSensitiveFinder csfinder = new CallerSensitiveFinder();
|
2013-04-16 21:39:52 -07:00
|
|
|
|
|
|
|
List<String> errors = csfinder.run(classes);
|
|
|
|
if (!errors.isEmpty()) {
|
|
|
|
throw new RuntimeException(errors.size() +
|
|
|
|
" caller-sensitive methods are missing @CallerSensitive annotation");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private final List<String> csMethodsMissingAnnotation = new ArrayList<>();
|
2013-04-30 15:42:16 -07:00
|
|
|
private final ReferenceFinder finder;
|
|
|
|
public CallerSensitiveFinder() {
|
|
|
|
this.finder = new ReferenceFinder(getFilter(), getVisitor());
|
|
|
|
}
|
|
|
|
|
|
|
|
private ReferenceFinder.Filter getFilter() {
|
|
|
|
final String classname = "sun/reflect/Reflection";
|
|
|
|
final String method = "getCallerClass";
|
|
|
|
return new ReferenceFinder.Filter() {
|
|
|
|
public boolean accept(ConstantPool cpool, CPRefInfo cpref) {
|
|
|
|
try {
|
|
|
|
CONSTANT_NameAndType_info nat = cpref.getNameAndTypeInfo();
|
|
|
|
return cpref.getClassName().equals(classname) && nat.getName().equals(method);
|
|
|
|
} catch (ConstantPoolException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private ReferenceFinder.Visitor getVisitor() {
|
|
|
|
return new ReferenceFinder.Visitor() {
|
|
|
|
public void visit(ClassFile cf, Method m, List<CPRefInfo> refs) {
|
|
|
|
try {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ConstantPoolException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2013-04-16 21:39:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public List<String> run(List<Path> classes) throws IOException, InterruptedException,
|
|
|
|
ExecutionException, ConstantPoolException
|
|
|
|
{
|
|
|
|
ExecutorService pool = Executors.newFixedThreadPool(numThreads);
|
|
|
|
for (Path path : classes) {
|
2013-10-17 13:22:11 -07:00
|
|
|
ClassFileReader reader = ClassFileReader.newInstance(path);
|
2013-04-16 21:39:52 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-04-30 15:42:16 -07:00
|
|
|
private final List<FutureTask<Void>> tasks = new ArrayList<FutureTask<Void>>();
|
|
|
|
private FutureTask<Void> getTask(final ClassFile cf) {
|
|
|
|
FutureTask<Void> task = new FutureTask<Void>(new Callable<Void>() {
|
|
|
|
public Void call() throws Exception {
|
|
|
|
finder.parse(cf);
|
|
|
|
return null;
|
2013-04-16 21:39:52 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
tasks.add(task);
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void waitForCompletion() throws InterruptedException, ExecutionException {
|
2013-04-30 15:42:16 -07:00
|
|
|
for (FutureTask<Void> t : tasks) {
|
|
|
|
t.get();
|
2013-04-16 21:39:52 -07:00
|
|
|
}
|
|
|
|
System.out.println("Parsed " + tasks.size() + " classfiles");
|
|
|
|
}
|
|
|
|
|
|
|
|
static class PlatformClassPath {
|
|
|
|
static List<Path> getJREClasses() throws IOException {
|
|
|
|
List<Path> result = new ArrayList<Path>();
|
|
|
|
Path home = Paths.get(System.getProperty("java.home"));
|
|
|
|
|
|
|
|
if (home.endsWith("jre")) {
|
|
|
|
// jar files in <javahome>/jre/lib
|
|
|
|
// skip <javahome>/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<Path> addJarFiles(final Path root) throws IOException {
|
|
|
|
final List<Path> result = new ArrayList<Path>();
|
|
|
|
final Path ext = root.resolve("ext");
|
|
|
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
|
|
|
@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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|