1/* 2 * Copyright (c) 2015, 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 24import com.sun.tools.classfile.*; 25import com.sun.tools.classfile.Field; 26import com.sun.tools.classfile.Method; 27 28import java.io.File; 29import java.io.FilenameFilter; 30import java.lang.reflect.*; 31import java.util.*; 32import java.util.function.Function; 33import java.util.function.Predicate; 34import java.util.function.Supplier; 35import java.util.stream.Collectors; 36import java.util.stream.Stream; 37 38/** 39 * The main class of Signature tests. 40 * Driver reads golden data of each class member that must have a Signature attribute, 41 * after that the class compares expected data with actual one. 42 * 43 * Example of usage Driver: 44 * java Driver Test 45 * 46 * Each member of the class Test should have @ExpectedSignature annotations 47 * if it must have the Signature attribute. Anonymous class cannot be annotated. 48 * So its enclosing class should be annotated and method isAnonymous 49 * of ExpectedSignature must return true. 50 */ 51public class Driver extends TestResult { 52 53 private final static String ACC_BRIDGE = "ACC_BRIDGE"; 54 55 private final String topLevelClassName; 56 private final File[] files; 57 58 public Driver(String topLevelClassName) { 59 this.topLevelClassName = topLevelClassName; 60 // Get top level class and all inner classes. 61 FilenameFilter filter = (dir, file) -> 62 file.equals(topLevelClassName + ".class") 63 || file.matches(topLevelClassName + "\\$.*\\.class"); 64 files = getClassDir().listFiles(filter); 65 } 66 67 private boolean isAnonymous(String className) { 68 return className.matches(".*\\$\\d+$"); 69 } 70 71 private Class<?> getEnclosingClass(String className) throws ClassNotFoundException { 72 return Class.forName(className.replaceFirst("\\$\\d+$", "")); 73 } 74 75 private ExpectedSignature getExpectedClassSignature(String className, Class<?> clazz) 76 throws ClassNotFoundException { 77 // anonymous class cannot be annotated, so information about anonymous class 78 // is located in its enclosing class. 79 boolean isAnonymous = isAnonymous(className); 80 clazz = isAnonymous ? getEnclosingClass(className) : clazz; 81 return Stream.of(clazz.getAnnotationsByType(ExpectedSignature.class)) 82 .filter(s -> s.isAnonymous() == isAnonymous) 83 .collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity())) 84 .get(className); 85 } 86 87 // Class.getName() cannot be used here, because the method can rely on signature attribute. 88 private Map<String, ExpectedSignature> getClassExpectedSignature(String className, Class<?> clazz) 89 throws ClassNotFoundException { 90 Map<String, ExpectedSignature> classSignatures = new HashMap<>(); 91 ExpectedSignature classSignature = getExpectedClassSignature(className, clazz); 92 if (classSignature != null) { 93 classSignatures.put(className, classSignature); 94 } 95 return classSignatures; 96 } 97 98 private Map<String, ExpectedSignature> getExpectedExecutableSignatures(Executable[] executables, 99 Predicate<Executable> filterBridge) { 100 return Stream.of(executables) 101 .filter(filterBridge) 102 .map(e -> e.getAnnotation(ExpectedSignature.class)) 103 .filter(Objects::nonNull) 104 .collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity())); 105 } 106 107 private Map<String, ExpectedSignature> getExpectedMethodSignatures(Class<?> clazz) { 108 Map<String, ExpectedSignature> methodSignatures = 109 getExpectedExecutableSignatures(clazz.getDeclaredMethods(), 110 m -> !((java.lang.reflect.Method) m).isBridge()); 111 methodSignatures.putAll( 112 getExpectedExecutableSignatures(clazz.getDeclaredConstructors(), 113 m -> true)); 114 return methodSignatures; 115 } 116 117 private Map<String, ExpectedSignature> getExpectedFieldSignatures(Class<?> clazz) { 118 return Stream.of(clazz.getDeclaredFields()) 119 .map(f -> f.getAnnotation(ExpectedSignature.class)) 120 .filter(Objects::nonNull) 121 .collect(Collectors.toMap(ExpectedSignature::descriptor, Function.identity())); 122 } 123 124 public void test() throws TestFailedException { 125 try { 126 addTestCase("Source is " + topLevelClassName + ".java"); 127 assertTrue(files.length > 0, "No class files found"); 128 for (File file : files) { 129 try { 130 String className = file.getName().replace(".class", ""); 131 Class<?> clazz = Class.forName(className); 132 printf("Testing class %s\n", className); 133 ClassFile classFile = readClassFile(file); 134 135 // test class signature 136 testAttribute( 137 className, 138 classFile, 139 () -> (Signature_attribute) classFile.getAttribute(Attribute.Signature), 140 getClassExpectedSignature(className, clazz).get(className)); 141 142 testFields(getExpectedFieldSignatures(clazz), classFile); 143 144 testMethods(getExpectedMethodSignatures(clazz), classFile); 145 } catch (Exception e) { 146 addFailure(e); 147 } 148 } 149 } catch (Exception e) { 150 addFailure(e); 151 } finally { 152 checkStatus(); 153 } 154 } 155 156 private void checkAllMembersFound(Set<String> found, Map<String, ExpectedSignature> signatures, String message) { 157 if (signatures != null) { 158 checkContains(found, 159 signatures.values().stream() 160 .map(ExpectedSignature::descriptor) 161 .collect(Collectors.toSet()), 162 message); 163 } 164 } 165 166 private void testMethods(Map<String, ExpectedSignature> expectedSignatures, ClassFile classFile) 167 throws ConstantPoolException, Descriptor.InvalidDescriptor { 168 String className = classFile.getName(); 169 Set<String> foundMethods = new HashSet<>(); 170 for (Method method : classFile.methods) { 171 String methodName = getMethodName(classFile, method); 172 printf("Testing method %s\n", methodName); 173 if (method.access_flags.getMethodFlags().contains(ACC_BRIDGE)) { 174 printf("Bridge method is skipped : %s\n", methodName); 175 continue; 176 } 177 testAttribute( 178 methodName, 179 classFile, 180 () -> (Signature_attribute) method.attributes.get(Attribute.Signature), 181 expectedSignatures.get(methodName)); 182 foundMethods.add(methodName); 183 } 184 checkAllMembersFound(foundMethods, expectedSignatures, 185 "Checking that all methods of class " + className + " with Signature attribute found"); 186 } 187 188 private String getMethodName(ClassFile classFile, Method method) 189 throws ConstantPoolException, Descriptor.InvalidDescriptor { 190 return String.format("%s%s", 191 method.getName(classFile.constant_pool), 192 method.descriptor.getParameterTypes(classFile.constant_pool)); 193 } 194 195 private void testFields(Map<String, ExpectedSignature> expectedSignatures, ClassFile classFile) 196 throws ConstantPoolException { 197 String className = classFile.getName(); 198 Set<String> foundFields = new HashSet<>(); 199 for (Field field : classFile.fields) { 200 String fieldName = field.getName(classFile.constant_pool); 201 printf("Testing field %s\n", fieldName); 202 testAttribute( 203 fieldName, 204 classFile, 205 () -> (Signature_attribute) field.attributes.get(Attribute.Signature), 206 expectedSignatures.get(fieldName)); 207 foundFields.add(fieldName); 208 } 209 checkAllMembersFound(foundFields, expectedSignatures, 210 "Checking that all fields of class " + className + " with Signature attribute found"); 211 } 212 213 private void testAttribute( 214 String memberName, 215 ClassFile classFile, 216 Supplier<Signature_attribute> sup, 217 ExpectedSignature expectedSignature) 218 throws ConstantPoolException { 219 220 Signature_attribute attribute = sup.get(); 221 if (expectedSignature != null && checkNotNull(attribute, memberName + " must have attribute")) { 222 checkEquals(classFile.constant_pool.getUTF8Value(attribute.attribute_name_index), 223 "Signature", "Attribute's name : " + memberName); 224 checkEquals(attribute.attribute_length, 2, "Attribute's length : " + memberName); 225 checkEquals(attribute.getSignature(classFile.constant_pool), 226 expectedSignature.signature(), 227 "Testing signature of : " + memberName); 228 } else { 229 checkNull(attribute, memberName + " must not have attribute"); 230 } 231 } 232 233 public static void main(String[] args) throws TestFailedException { 234 if (args.length != 1) { 235 throw new IllegalArgumentException("Usage: Driver <class-name>"); 236 } 237 new Driver(args[0]).test(); 238 } 239}