blob: 800851863f407141624be022b1bacf681e7ea3f4 [file] [log] [blame]
/*
* Copyright (c) 2015, 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.classfile.Field;
import com.sun.tools.classfile.Method;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The main class of Signature tests.
* Driver reads golden data of each class member that must have a Signature attribute,
* after that the class compares expected data with actual one.
*
* Example of usage Driver:
* java Driver Test
*
* Each member of the class Test should have @ExpectedSignature annotations
* if it must have the Signature attribute. Anonymous class cannot be annotated.
* So its enclosing class should be annotated and method isAnonymous
* of ExpectedSignature must return true.
*/
public class Driver extends TestResult {
private final static String ACC_BRIDGE = "ACC_BRIDGE";
private final String topLevelClassName;
private final File[] files;
public Driver(String topLevelClassName) {
this.topLevelClassName = topLevelClassName;
// Get top level class and all inner classes.
FilenameFilter filter = (dir, file) ->
file.equals(topLevelClassName + ".class")
|| file.matches(topLevelClassName + "\\$.*\\.class");
files = getClassDir().listFiles(filter);
}
private boolean isAnonymous(String className) {
return className.matches(".*\\$\\d+$");
}
private Class<?> getEnclosingClass(String className) throws ClassNotFoundException {
return Class.forName(className.replaceFirst("\\$\\d+$", ""));
}
private ExpectedSignature getExpectedClassSignature(String className, Class<?> clazz)
throws ClassNotFoundException {
// anonymous class cannot be annotated, so information about anonymous class
// is located in its enclosing class.
boolean isAnonymous = isAnonymous(className);
clazz = isAnonymous ? getEnclosingClass(className) : clazz;
return Stream.of(clazz.getAnnotationsByType(ExpectedSignature.class))
.filter(s -> s.isAnonymous() == isAnonymous)
.collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity()))
.get(className);
}
// Class.getName() cannot be used here, because the method can rely on signature attribute.
private Map<String, ExpectedSignature> getClassExpectedSignature(String className, Class<?> clazz)
throws ClassNotFoundException {
Map<String, ExpectedSignature> classSignatures = new HashMap<>();
ExpectedSignature classSignature = getExpectedClassSignature(className, clazz);
if (classSignature != null) {
classSignatures.put(className, classSignature);
}
return classSignatures;
}
private Map<String, ExpectedSignature> getExpectedExecutableSignatures(Executable[] executables,
Predicate<Executable> filterBridge) {
return Stream.of(executables)
.filter(filterBridge)
.map(e -> e.getAnnotation(ExpectedSignature.class))
.filter(Objects::nonNull)
.collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity()));
}
private Map<String, ExpectedSignature> getExpectedMethodSignatures(Class<?> clazz) {
Map<String, ExpectedSignature> methodSignatures =
getExpectedExecutableSignatures(clazz.getDeclaredMethods(),
m -> !((java.lang.reflect.Method) m).isBridge());
methodSignatures.putAll(
getExpectedExecutableSignatures(clazz.getDeclaredConstructors(),
m -> true));
return methodSignatures;
}
private Map<String, ExpectedSignature> getExpectedFieldSignatures(Class<?> clazz) {
return Stream.of(clazz.getDeclaredFields())
.map(f -> f.getAnnotation(ExpectedSignature.class))
.filter(Objects::nonNull)
.collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity()));
}
public void test() throws TestFailedException {
try {
addTestCase("Source is " + topLevelClassName + ".java");
assertTrue(files.length > 0, "No class files found");
for (File file : files) {
try {
String className = file.getName().replace(".class", "");
Class<?> clazz = Class.forName(className);
printf("Testing class %s\n", className);
ClassFile classFile = readClassFile(file);
// test class signature
testAttribute(
className,
classFile,
() -> (Signature_attribute) classFile.getAttribute(Attribute.Signature),
getClassExpectedSignature(className, clazz).get(className));
testFields(getExpectedFieldSignatures(clazz), classFile);
testMethods(getExpectedMethodSignatures(clazz), classFile);
} catch (Exception e) {
addFailure(e);
}
}
} catch (Exception e) {
addFailure(e);
} finally {
checkStatus();
}
}
private void checkAllMembersFound(Set<String> found, Map<String, ExpectedSignature> signatures, String message) {
if (signatures != null) {
checkContains(found,
signatures.values().stream()
.map(ExpectedSignature::descriptor)
.collect(Collectors.toSet()),
message);
}
}
private void testMethods(Map<String, ExpectedSignature> expectedSignatures, ClassFile classFile)
throws ConstantPoolException, Descriptor.InvalidDescriptor {
String className = classFile.getName();
Set<String> foundMethods = new HashSet<>();
for (Method method : classFile.methods) {
String methodName = getMethodName(classFile, method);
printf("Testing method %s\n", methodName);
if (method.access_flags.getMethodFlags().contains(ACC_BRIDGE)) {
printf("Bridge method is skipped : %s\n", methodName);
continue;
}
testAttribute(
methodName,
classFile,
() -> (Signature_attribute) method.attributes.get(Attribute.Signature),
expectedSignatures.get(methodName));
foundMethods.add(methodName);
}
checkAllMembersFound(foundMethods, expectedSignatures,
"Checking that all methods of class " + className + " with Signature attribute found");
}
private String getMethodName(ClassFile classFile, Method method)
throws ConstantPoolException, Descriptor.InvalidDescriptor {
return String.format("%s%s",
method.getName(classFile.constant_pool),
method.descriptor.getParameterTypes(classFile.constant_pool));
}
private void testFields(Map<String, ExpectedSignature> expectedSignatures, ClassFile classFile)
throws ConstantPoolException {
String className = classFile.getName();
Set<String> foundFields = new HashSet<>();
for (Field field : classFile.fields) {
String fieldName = field.getName(classFile.constant_pool);
printf("Testing field %s\n", fieldName);
testAttribute(
fieldName,
classFile,
() -> (Signature_attribute) field.attributes.get(Attribute.Signature),
expectedSignatures.get(fieldName));
foundFields.add(fieldName);
}
checkAllMembersFound(foundFields, expectedSignatures,
"Checking that all fields of class " + className + " with Signature attribute found");
}
private void testAttribute(
String memberName,
ClassFile classFile,
Supplier<Signature_attribute> sup,
ExpectedSignature expectedSignature)
throws ConstantPoolException {
Signature_attribute attribute = sup.get();
if (expectedSignature != null && checkNotNull(attribute, memberName + " must have attribute")) {
checkEquals(classFile.constant_pool.getUTF8Value(attribute.attribute_name_index),
"Signature", "Attribute's name : " + memberName);
checkEquals(attribute.attribute_length, 2, "Attribute's length : " + memberName);
checkEquals(attribute.getSignature(classFile.constant_pool),
expectedSignature.signature(),
"Testing signature of : " + memberName);
} else {
checkNull(attribute, memberName + " must not have attribute");
}
}
public static void main(String[] args) throws TestFailedException {
if (args.length != 1) {
throw new IllegalArgumentException("Usage: Driver <class-name>");
}
new Driver(args[0]).test();
}
}