1 /* 2 * Copyright (c) 2014, 2022, 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 if (outerClassType == ClassType.CLASS && outerMod.contains(Modifier.ABSTRACT)) 281 sb.append("int f;\n"); // impose identity to make testing predicatable 282 int count = 0; 283 Map<String, Set<String>> class2Flags = new HashMap<>(); 284 List<String> syntheticClasses = new ArrayList<>(); 285 for (List<Modifier> innerMod : innerMods) { 286 ++count; 287 String privateConstructor = ""; 288 if (hasSyntheticClass && !innerMod.contains(Modifier.ABSTRACT)) { 289 privateConstructor = "private A" + count + "() {}"; 290 syntheticClasses.add("new A" + count + "();"); 291 } 292 String instField = innerClassType == ClassType.CLASS && innerMod.contains(Modifier.ABSTRACT) ? 293 "int f; " : ""; // impose identity to make testing predicatable 294 sb.append(toString(innerMod)).append(' '); 295 sb.append(String.format("%s A%d { %s %s}\n", innerClassType, count, instField, privateConstructor)); 296 Set<String> flags = getFlags(innerClassType, innerMod); 297 class2Flags.put("A" + count, flags); 298 } 299 if (hasSyntheticClass) { 300 // Source to generate synthetic classes 301 sb.append(syntheticClasses.stream().collect(Collectors.joining(" ", "{", "}"))); 302 class2Flags.put("1", new HashSet<>(Arrays.asList("ACC_STATIC", "ACC_IDENTITY", "ACC_SYNTHETIC"))); 303 } 304 sb.append(suffix).append("\n}"); 305 getAdditionalFlags(class2Flags, outerClassType, outerMod.toArray(new Modifier[outerMod.size()])); 306 list.add(new TestCase(sb.toString(), class2Flags)); 307 } 308 return list; 309 } 310 311 /** 312 * Methods returns flags which must have type. 313 * 314 * @param type class, interface, enum or annotation 315 * @param mods modifiers 316 * @return set of access flags 317 */ 318 protected Set<String> getFlags(ClassType type, List<Modifier> mods) { 319 Set<String> flags = mods.stream() 320 .map(Modifier::getString) 321 .filter(str -> !str.isEmpty()) 322 .map(str -> "ACC_" + str.toUpperCase()) 323 .collect(Collectors.toSet()); 324 type.addSpecificFlags(flags); 325 return flags; 326 } 327 328 protected List<String> getCompileOptions() { 329 return Collections.emptyList(); 330 } 331 332 private List<List<Modifier>> getAllCombinations(Modifier[] accessModifiers, Modifier[] otherModifiers) { 333 List<List<Modifier>> list = new ArrayList<>(); 334 for (Modifier access : accessModifiers) { 335 for (int i = 0; i < otherModifiers.length; ++i) { 336 Modifier mod1 = otherModifiers[i]; 337 for (int j = i + 1; j < otherModifiers.length; ++j) { 338 Modifier mod2 = otherModifiers[j]; 339 if (isForbidden(mod1, mod2)) { 340 continue; 341 } 342 list.add(Arrays.asList(access, mod1, mod2)); 343 } 344 if (mod1 == Modifier.EMPTY) { 345 list.add(Collections.singletonList(access)); 346 } 347 } 348 } 349 return list; 350 } 351 352 private boolean isForbidden(Modifier mod1, Modifier mod2) { 353 return mod1 == Modifier.FINAL && mod2 == Modifier.ABSTRACT 354 || mod1 == Modifier.ABSTRACT && mod2 == Modifier.FINAL; 355 } 356 357 private String toString(List<Modifier> mods) { 358 return mods.stream() 359 .map(Modifier::getString) 360 .filter(s -> !s.isEmpty()) 361 .collect(Collectors.joining(" ")); 362 } 363 364 /** 365 * Method is called in generateTestCases(). 366 * If you need to add additional access flags, you should override this method. 367 * 368 * 369 * @param class2Flags map with flags 370 * @param type class, interface, enum or @annotation 371 * @param mods modifiers 372 */ 373 public void getAdditionalFlags(Map<String, Set<String>> class2Flags, ClassType type, Modifier...mods) { 374 class2Flags.values().forEach(type::addFlags); 375 } 376 377 public enum ClassType { 378 CLASS("class") { 379 @Override 380 public void addSpecificFlags(Set<String> flags) { 381 flags.add("ACC_IDENTITY"); 382 } 383 }, 384 INTERFACE("interface") { 385 @Override 386 public void addFlags(Set<String> flags) { 387 flags.add("ACC_STATIC"); 388 flags.add("ACC_PUBLIC"); 389 } 390 391 @Override 392 public void addSpecificFlags(Set<String> flags) { 393 flags.add("ACC_INTERFACE"); 394 flags.add("ACC_ABSTRACT"); 395 flags.add("ACC_STATIC"); 396 } 397 }, 398 ANNOTATION("@interface") { 399 @Override 400 public void addFlags(Set<String> flags) { 401 flags.add("ACC_STATIC"); 402 flags.add("ACC_PUBLIC"); 403 } 404 405 @Override 406 public void addSpecificFlags(Set<String> flags) { 407 flags.add("ACC_INTERFACE"); 408 flags.add("ACC_ABSTRACT"); 409 flags.add("ACC_STATIC"); 410 flags.add("ACC_ANNOTATION"); 411 } 412 }, 413 ENUM("enum") { 414 @Override 415 public void addSpecificFlags(Set<String> flags) { 416 flags.add("ACC_ENUM"); 417 flags.add("ACC_FINAL"); 418 flags.add("ACC_STATIC"); 419 flags.add("ACC_IDENTITY"); 420 } 421 }, 422 OTHER("") { 423 @Override 424 public void addSpecificFlags(Set<String> flags) { 425 flags.add("ACC_IDENTITY"); 426 } 427 }; 428 429 private final String classType; 430 431 ClassType(String clazz) { 432 this.classType = clazz; 433 } 434 435 public abstract void addSpecificFlags(Set<String> flags); 436 437 public String toString() { 438 return classType; 439 } 440 441 public void addFlags(Set<String> set) { 442 } 443 } 444 445 public enum Modifier { 446 PUBLIC("public"), PRIVATE("private"), 447 PROTECTED("protected"), DEFAULT("default"), 448 FINAL("final"), ABSTRACT("abstract"), 449 STATIC("static"), EMPTY(""), 450 IDENTITY("identity"); 451 452 private final String str; 453 454 Modifier(String str) { 455 this.str = str; 456 } 457 458 public String getString() { 459 return str; 460 } 461 } 462 }