1 /* 2 * Copyright (c) 2014, 2018, 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 24 import java.lang.classfile.*; 25 import java.lang.classfile.attribute.*; 26 import java.lang.classfile.constantpool.*; 27 import jdk.internal.classfile.impl.BoundAttribute; 28 29 import java.nio.file.Paths; 30 import java.util.*; 31 import java.util.stream.Collectors; 32 import java.lang.reflect.AccessFlag; 33 34 /** 35 * Base class for tests of inner classes attribute. 36 * The scenario of tests: 37 * 1. set possible values of class modifiers. 38 * 2. according to set class modifiers, a test generates sources 39 * and golden data with {@code generateTestCases}. 40 * 3. a test loops through all test cases and checks InnerClasses 41 * attribute with {@code test}. 42 * 43 * Example, possible flags for outer class are {@code Modifier.PRIVATE and Modifier.PUBLIC}, 44 * possible flags for inner class are {@code Modifier.EMPTY}. 45 * At the second step the test generates two test cases: 46 * 1. public class A { 47 * public class B { 48 * class C {} 49 * } 50 * } 51 * 2. public class A { 52 * private class B { 53 * class C {} 54 * } 55 * } 56 */ 57 public abstract class InnerClassesTestBase extends TestResult { 58 59 private Modifier[] outerAccessModifiers = {Modifier.EMPTY, Modifier.PRIVATE, Modifier.PROTECTED, Modifier.PUBLIC}; 60 private Modifier[] outerOtherModifiers = {Modifier.EMPTY, Modifier.STATIC, Modifier.FINAL, Modifier.ABSTRACT}; 61 private Modifier[] innerAccessModifiers = outerAccessModifiers; 62 private Modifier[] innerOtherModifiers = outerOtherModifiers; 63 private boolean isForbiddenWithoutStaticInOuterMods = false; 64 65 private ClassType outerClassType; 66 private ClassType innerClassType; 67 private boolean hasSyntheticClass; 68 private String prefix = ""; 69 private String suffix = ""; 70 71 /** 72 * Sets properties. 73 * 74 * Returns generated list of test cases. Method is called in {@code test()}. 75 */ 76 public abstract void setProperties(); 77 78 /** 79 * Runs the test. 80 * 81 * @param classToTest expected name of outer class 82 * @param skipClasses classes that names should not be checked 83 */ 84 public void test(String classToTest, String...skipClasses) throws TestFailedException { 85 try { 86 String testName = getClass().getName(); 87 List<TestCase> testCases = generateTestCases(); 88 for (int i = 0; i < testCases.size(); ++i) { 89 TestCase test = testCases.get(i); 90 String testCaseName = testName + i + ".java"; 91 addTestCase(testCaseName); 92 writeToFileIfEnabled(Paths.get(testCaseName), test.getSource()); 93 test(classToTest, test, skipClasses); 94 } 95 } catch (Exception e) { 96 addFailure(e); 97 } finally { 98 checkStatus(); 99 } 100 } 101 102 /** 103 * If {@code flag} is {@code true} an outer class can not have static modifier. 104 * 105 * @param flag if {@code true} the outer class can not have static modifier 106 */ 107 public void setForbiddenWithoutStaticInOuterMods(boolean flag) { 108 isForbiddenWithoutStaticInOuterMods = flag; 109 } 110 111 /** 112 * Sets the possible access flags of an outer class. 113 * 114 * @param mods the possible access flags of an outer class 115 */ 116 public void setOuterAccessModifiers(Modifier...mods) { 117 outerAccessModifiers = mods; 118 } 119 120 /** 121 * Sets the possible flags of an outer class. 122 * 123 * @param mods the possible flags of an outer class 124 */ 125 public void setOuterOtherModifiers(Modifier...mods) { 126 outerOtherModifiers = mods; 127 } 128 129 /** 130 * Sets the possible access flags of an inner class. 131 * 132 * @param mods the possible access flags of an inner class 133 */ 134 public void setInnerAccessModifiers(Modifier...mods) { 135 innerAccessModifiers = mods; 136 } 137 138 /** 139 * Sets the possible flags of an inner class. 140 * 141 * @param mods the possible flags of an inner class 142 */ 143 public void setInnerOtherModifiers(Modifier...mods) { 144 innerOtherModifiers = mods; 145 } 146 147 /** 148 * Sets the suffix for the generated source. 149 * 150 * @param suffix a suffix 151 */ 152 public void setSuffix(String suffix) { 153 this.suffix = suffix; 154 } 155 156 /** 157 * Sets the prefix for the generated source. 158 * 159 * @param prefix a prefix 160 */ 161 public void setPrefix(String prefix) { 162 this.prefix = prefix; 163 } 164 165 /** 166 * If {@code true} synthetic class is generated. 167 * 168 * @param hasSyntheticClass if {@code true} synthetic class is generated 169 */ 170 public void setHasSyntheticClass(boolean hasSyntheticClass) { 171 this.hasSyntheticClass = hasSyntheticClass; 172 } 173 174 /** 175 * Sets the inner class type. 176 * 177 * @param innerClassType the inner class type 178 */ 179 public void setInnerClassType(ClassType innerClassType) { 180 this.innerClassType = innerClassType; 181 } 182 183 /** 184 * Sets the outer class type. 185 * 186 * @param outerClassType the outer class type 187 */ 188 public void setOuterClassType(ClassType outerClassType) { 189 this.outerClassType = outerClassType; 190 } 191 192 private void test(String classToTest, TestCase test, String...skipClasses) { 193 printf("Testing :\n%s\n", test.getSource()); 194 try { 195 Map<String, Set<String>> class2Flags = test.getFlags(); 196 ClassModel cm = readClassFile(compile(getCompileOptions(), test.getSource()) 197 .getClasses().get(classToTest)); 198 InnerClassesAttribute innerClasses = cm.findAttribute(Attributes.innerClasses()).orElse(null); 199 int count = 0; 200 for (Attribute<?> a : cm.attributes()) { 201 if (a instanceof InnerClassesAttribute) { 202 ++count; 203 } 204 } 205 checkEquals(1, count, "Number of inner classes attribute"); 206 if (!checkNotNull(innerClasses, "InnerClasses attribute should not be null")) { 207 return; 208 } 209 checkEquals(innerClasses.attributeName(), "InnerClasses", 210 "innerClasses.attribute_name_index"); 211 // Inner Classes attribute consists of length (2 bytes) 212 // and 8 bytes for each inner class's entry. 213 checkEquals(((BoundAttribute<?>)innerClasses).payloadLen(), 214 2 + 8 * class2Flags.size(), "innerClasses.attribute_length"); 215 checkEquals(innerClasses.classes().size(), 216 class2Flags.size(), "innerClasses.number_of_classes"); 217 Set<String> visitedClasses = new HashSet<>(); 218 for (InnerClassInfo e : innerClasses.classes()) { 219 String baseName = e.innerClass().asInternalName(); 220 if (cm.majorVersion() >= 51 && e.innerClass().index() == 0) { 221 ClassEntry out = e.outerClass().orElse(null); 222 // The outer_class_info_index of sun.tools.classfile will return 0 if it is not a member of a class or interface. 223 checkEquals(out == null? 0: out.index(), 0, 224 "outer_class_info_index " 225 + "in case of inner_name_index is zero : " 226 + baseName); 227 } 228 String className = baseName.replaceFirst(".*\\$", ""); 229 checkTrue(class2Flags.containsKey(className), 230 className); 231 checkTrue(visitedClasses.add(className), 232 "there are no duplicates in attribute : " + className); 233 //Convert the Set<string> to Set<AccessFlag> 234 Set<AccessFlag> accFlags = class2Flags.get(className).stream() 235 .map(str -> AccessFlag.valueOf(str.substring(str.indexOf("_") + 1))) 236 .collect(Collectors.toSet()); 237 checkEquals(e.flags(), 238 accFlags, 239 "inner_class_access_flags " + className); 240 if (!Arrays.asList(skipClasses).contains(className)) { 241 checkEquals( 242 e.innerClass().asInternalName(), 243 classToTest + "$" + className, 244 "inner_class_info_index of " + className); 245 if (e.outerClass().orElse(null) != null && e.outerClass().get().index() > 0) { 246 checkEquals( 247 e.outerClass().get().name().stringValue(), 248 classToTest, 249 "outer_class_info_index of " + className); 250 } 251 } 252 } 253 } catch (Exception e) { 254 addFailure(e); 255 } 256 } 257 258 /** 259 * Methods generates list of test cases. Method generates all possible combinations 260 * of acceptable flags for nested inner classes. 261 * 262 * @return generated list of test cases 263 */ 264 protected List<TestCase> generateTestCases() { 265 setProperties(); 266 List<TestCase> list = new ArrayList<>(); 267 268 List<List<Modifier>> outerMods = getAllCombinations(outerAccessModifiers, outerOtherModifiers); 269 List<List<Modifier>> innerMods = getAllCombinations(innerAccessModifiers, innerOtherModifiers); 270 271 for (List<Modifier> outerMod : outerMods) { 272 if (isForbiddenWithoutStaticInOuterMods && !outerMod.contains(Modifier.STATIC)) { 273 continue; 274 } 275 StringBuilder sb = new StringBuilder(); 276 sb.append("public class InnerClassesSrc {") 277 .append(toString(outerMod)).append(' ') 278 .append(outerClassType).append(' ') 279 .append(prefix).append(' ').append('\n'); 280 int count = 0; 281 Map<String, Set<String>> class2Flags = new HashMap<>(); 282 List<String> syntheticClasses = new ArrayList<>(); 283 for (List<Modifier> innerMod : innerMods) { 284 ++count; 285 String privateConstructor = ""; 286 if (hasSyntheticClass && !innerMod.contains(Modifier.ABSTRACT)) { 287 privateConstructor = "private A" + count + "() {}"; 288 syntheticClasses.add("new A" + count + "();"); 289 } 290 sb.append(toString(innerMod)).append(' '); 291 sb.append(String.format("%s A%d {%s}\n", innerClassType, count, privateConstructor)); 292 Set<String> flags = getFlags(innerClassType, innerMod); 293 class2Flags.put("A" + count, flags); 294 } 295 if (hasSyntheticClass) { 296 // Source to generate synthetic classes 297 sb.append(syntheticClasses.stream().collect(Collectors.joining(" ", "{", "}"))); 298 class2Flags.put("1", new HashSet<>(Arrays.asList("ACC_STATIC", "ACC_SYNTHETIC"))); 299 } 300 sb.append(suffix).append("\n}"); 301 getAdditionalFlags(class2Flags, outerClassType, outerMod.toArray(new Modifier[outerMod.size()])); 302 list.add(new TestCase(sb.toString(), class2Flags)); 303 } 304 return list; 305 } 306 307 /** 308 * Methods returns flags which must have type. 309 * 310 * @param type class, interface, enum or annotation 311 * @param mods modifiers 312 * @return set of access flags 313 */ 314 protected Set<String> getFlags(ClassType type, List<Modifier> mods) { 315 Set<String> flags = mods.stream() 316 .map(Modifier::getString) 317 .filter(str -> !str.isEmpty()) 318 .map(str -> "ACC_" + str.toUpperCase()) 319 .collect(Collectors.toSet()); 320 type.addSpecificFlags(flags); 321 return flags; 322 } 323 324 protected List<String> getCompileOptions() { 325 return Collections.emptyList(); 326 } 327 328 private List<List<Modifier>> getAllCombinations(Modifier[] accessModifiers, Modifier[] otherModifiers) { 329 List<List<Modifier>> list = new ArrayList<>(); 330 for (Modifier access : accessModifiers) { 331 for (int i = 0; i < otherModifiers.length; ++i) { 332 Modifier mod1 = otherModifiers[i]; 333 for (int j = i + 1; j < otherModifiers.length; ++j) { 334 Modifier mod2 = otherModifiers[j]; 335 if (isForbidden(mod1, mod2)) { 336 continue; 337 } 338 list.add(Arrays.asList(access, mod1, mod2)); 339 } 340 if (mod1 == Modifier.EMPTY) { 341 list.add(Collections.singletonList(access)); 342 } 343 } 344 } 345 return list; 346 } 347 348 private boolean isForbidden(Modifier mod1, Modifier mod2) { 349 return mod1 == Modifier.FINAL && mod2 == Modifier.ABSTRACT 350 || mod1 == Modifier.ABSTRACT && mod2 == Modifier.FINAL; 351 } 352 353 private String toString(List<Modifier> mods) { 354 return mods.stream() 355 .map(Modifier::getString) 356 .filter(s -> !s.isEmpty()) 357 .collect(Collectors.joining(" ")); 358 } 359 360 /** 361 * Method is called in generateTestCases(). 362 * If you need to add additional access flags, you should override this method. 363 * 364 * 365 * @param class2Flags map with flags 366 * @param type class, interface, enum or @annotation 367 * @param mods modifiers 368 */ 369 public void getAdditionalFlags(Map<String, Set<String>> class2Flags, ClassType type, Modifier...mods) { 370 class2Flags.values().forEach(type::addFlags); 371 } 372 373 public enum ClassType { 374 CLASS("class") { 375 @Override 376 public void addSpecificFlags(Set<String> flags) { 377 } 378 }, 379 INTERFACE("interface") { 380 @Override 381 public void addFlags(Set<String> flags) { 382 flags.add("ACC_STATIC"); 383 flags.add("ACC_PUBLIC"); 384 } 385 386 @Override 387 public void addSpecificFlags(Set<String> flags) { 388 flags.add("ACC_INTERFACE"); 389 flags.add("ACC_ABSTRACT"); 390 flags.add("ACC_STATIC"); 391 } 392 }, 393 ANNOTATION("@interface") { 394 @Override 395 public void addFlags(Set<String> flags) { 396 flags.add("ACC_STATIC"); 397 flags.add("ACC_PUBLIC"); 398 } 399 400 @Override 401 public void addSpecificFlags(Set<String> flags) { 402 flags.add("ACC_INTERFACE"); 403 flags.add("ACC_ABSTRACT"); 404 flags.add("ACC_STATIC"); 405 flags.add("ACC_ANNOTATION"); 406 } 407 }, 408 ENUM("enum") { 409 @Override 410 public void addSpecificFlags(Set<String> flags) { 411 flags.add("ACC_ENUM"); 412 flags.add("ACC_FINAL"); 413 flags.add("ACC_STATIC"); 414 } 415 }, 416 OTHER("") { 417 @Override 418 public void addSpecificFlags(Set<String> flags) { 419 } 420 }; 421 422 private final String classType; 423 424 ClassType(String clazz) { 425 this.classType = clazz; 426 } 427 428 public abstract void addSpecificFlags(Set<String> flags); 429 430 public String toString() { 431 return classType; 432 } 433 434 public void addFlags(Set<String> set) { 435 } 436 } 437 438 public enum Modifier { 439 PUBLIC("public"), PRIVATE("private"), 440 PROTECTED("protected"), DEFAULT("default"), 441 FINAL("final"), ABSTRACT("abstract"), 442 STATIC("static"), EMPTY(""); 443 444 private final String str; 445 446 Modifier(String str) { 447 this.str = str; 448 } 449 450 public String getString() { 451 return str; 452 } 453 } 454 }