1 /* 2 * Copyright (c) 2022, 2025, 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 /* 25 * @test 26 * @bug 8289106 8293627 27 * @summary Tests of AccessFlag.locations(ClassFileFormatVersion) and 28 * accessors on AccessFlag.Location 29 */ 30 31 import java.lang.reflect.AccessFlag; 32 import static java.lang.reflect.AccessFlag.*; 33 import java.lang.reflect.ClassFileFormatVersion; 34 import java.util.HashSet; 35 import java.util.Set; 36 37 /* 38 * There are several patterns of access flag applicability. First, an 39 * access flag can be applied to the same set of locations for each 40 * class file format version. This is "invariant" usage. Second, an 41 * access flag can be defined for version N, therefore inapplicable 42 * for earlier versions, and then applied to the same locations for 43 * all subsequent versions. This is "step" usage. Finally, an access 44 * flag to have a more complicated pattern, having multiple steps of 45 * being allowed at more locations or even having locations removed if 46 * the access flag is retired. 47 * 48 * List of access flags and how they are tested: 49 * 50 * PUBLIC step 51 * PRIVATE step 52 * PROTECTED step 53 * STATIC step 54 * FINAL two-step 55 * SUPER step 56 * OPEN step 57 * TRANSITIVE step 58 * SYNCHRONIZED invariant 59 * STATIC_PHASE step 60 * VOLATILE invariant 61 * BRIDGE step 62 * TRANSIENT invariant 63 * VARARGS step 64 * NATIVE invariant 65 * INTERFACE step 66 * ABSTRACT step 67 * STRICT other 68 * SYNTHETIC other (three-step) 69 * ANNOTATION step 70 * ENUM step 71 * MANDATED two-step 72 * MODULE step 73 */ 74 75 public class VersionedLocationsTest { 76 public static void main(String... args) throws Exception { 77 testInvariantAccessFlags(); 78 testStepFunctionAccessFlags(); 79 testTwoStepAccessFlags(); 80 testSynthetic(); 81 testStrict(); 82 testLatestMatch(); 83 testFlagVersionConsistency(); 84 testLocationMaskFlagConsistency(); 85 } 86 87 /** 88 * Invariant access flags have the same set of locations for each 89 * class file format version. 90 */ 91 private static void testInvariantAccessFlags() { 92 Set<AccessFlag> invariantAccessFlags = 93 Set.of(SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE); 94 for(var accessFlag : invariantAccessFlags) { 95 Set<AccessFlag.Location> expected = accessFlag.locations(); 96 97 for(var cffv : ClassFileFormatVersion.values()) { 98 compareLocations(accessFlag.locations(), accessFlag, cffv); 99 } 100 } 101 } 102 103 private static void testStepFunctionAccessFlags() { 104 StepFunctionTC[] testCases = { 105 new StepFunctionTC(PUBLIC, 106 removeInnerClass(PUBLIC.locations()), 107 ClassFileFormatVersion.RELEASE_1), 108 109 new StepFunctionTC(PRIVATE, 110 removeInnerClass(PRIVATE.locations()), 111 ClassFileFormatVersion.RELEASE_1), 112 113 new StepFunctionTC(PROTECTED, 114 removeInnerClass(PROTECTED.locations()), 115 ClassFileFormatVersion.RELEASE_1), 116 117 new StepFunctionTC(STATIC, 118 removeInnerClass(STATIC.locations()), 119 ClassFileFormatVersion.RELEASE_1), 120 121 new StepFunctionTC(SUPER, 122 Set.of(AccessFlag.Location.CLASS), 123 ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES), 124 125 new StepFunctionTC(IDENTITY, 126 Set.of(), 127 ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES), 128 129 new StepFunctionTC(STRICT_INIT, 130 Set.of(), 131 ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES), 132 133 new StepFunctionTC(OPEN, 134 Set.of(), 135 ClassFileFormatVersion.RELEASE_9), 136 137 new StepFunctionTC(TRANSITIVE, 138 Set.of(), 139 ClassFileFormatVersion.RELEASE_9), 140 141 new StepFunctionTC(STATIC_PHASE, 142 Set.of(), 143 ClassFileFormatVersion.RELEASE_9), 144 145 new StepFunctionTC(BRIDGE, 146 Set.of(), 147 ClassFileFormatVersion.RELEASE_5), 148 149 new StepFunctionTC(VARARGS, 150 Set.of(), 151 ClassFileFormatVersion.RELEASE_5), 152 153 new StepFunctionTC(INTERFACE, 154 removeInnerClass(INTERFACE.locations()), 155 ClassFileFormatVersion.RELEASE_1), 156 157 new StepFunctionTC(ABSTRACT, 158 removeInnerClass(ABSTRACT.locations()), 159 ClassFileFormatVersion.RELEASE_1), 160 161 new StepFunctionTC(ANNOTATION, 162 Set.of(), 163 ClassFileFormatVersion.RELEASE_5), 164 165 new StepFunctionTC(ENUM, 166 Set.of(), 167 ClassFileFormatVersion.RELEASE_5), 168 169 new StepFunctionTC(MODULE, 170 Set.of(), 171 ClassFileFormatVersion.RELEASE_9) 172 }; 173 174 for (var testCase : testCases) { 175 for (var cffv : ClassFileFormatVersion.values()) { 176 compareLocations(cffv.compareTo(testCase.transition()) >= 0 ? 177 testCase.finalLocs() : 178 testCase.initialLocs(), 179 testCase.accessFlag, cffv); 180 } 181 } 182 } 183 184 private static void compareLocations(Set<AccessFlag.Location> expected, 185 AccessFlag accessFlag, 186 ClassFileFormatVersion cffv) { 187 var actual = accessFlag.locations(cffv); 188 if (!expected.equals(actual)) { 189 throw new RuntimeException("Unexpected locations for " + 190 accessFlag + " on " + cffv + "\n" + 191 "Expected " + expected + "; got \t" + actual); 192 } 193 } 194 195 private static Set<AccessFlag.Location> removeInnerClass(Set<AccessFlag.Location> locations) { 196 var s = new HashSet<>(locations); 197 s.remove(Location.INNER_CLASS); 198 return s; 199 } 200 201 private record StepFunctionTC(AccessFlag accessFlag, 202 Set<AccessFlag.Location> initialLocs, 203 ClassFileFormatVersion transition) { 204 205 public Set<AccessFlag.Location> finalLocs() { 206 return accessFlag.locations(ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES); 207 } 208 } 209 210 211 private record TwoStepFunctionTC(AccessFlag accessFlag, 212 Set<AccessFlag.Location> initialLocs, 213 ClassFileFormatVersion transition1, 214 Set<AccessFlag.Location> firstLocs, 215 ClassFileFormatVersion transition2) { 216 217 public Set<AccessFlag.Location> secondLocs() { 218 return accessFlag.locations(); 219 } 220 } 221 222 private static void testTwoStepAccessFlags() { 223 TwoStepFunctionTC[] testCases = { 224 new TwoStepFunctionTC(FINAL, 225 Set.of(Location.CLASS, Location.FIELD, Location.METHOD), 226 ClassFileFormatVersion.RELEASE_1, 227 Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS), 228 ClassFileFormatVersion.RELEASE_8), 229 230 new TwoStepFunctionTC(MANDATED, 231 Set.of(), 232 ClassFileFormatVersion.RELEASE_8, 233 Set.of(Location.METHOD_PARAMETER), 234 ClassFileFormatVersion.RELEASE_9), 235 }; 236 237 for (var testCase : testCases) { 238 for (var cffv : ClassFileFormatVersion.values()) { 239 var transition1 = testCase.transition1(); 240 var transition2 = testCase.transition2(); 241 Set<AccessFlag.Location> expected; 242 if (cffv.compareTo(transition1) < 0) { 243 expected = testCase.initialLocs(); 244 } else if (cffv.compareTo(transition1) >= 0 && 245 cffv.compareTo(transition2) < 0) { 246 expected = testCase.firstLocs(); 247 } else { // cffv >= transition2 248 expected = testCase.secondLocs(); 249 } 250 251 compareLocations(expected, testCase.accessFlag(), cffv); 252 } 253 } 254 } 255 256 private static void testSynthetic() { 257 for (var cffv : ClassFileFormatVersion.values()) { 258 Set<AccessFlag.Location> expected; 259 if (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) < 0) { 260 expected = Set.of(); 261 } else { 262 expected = 263 switch(cffv) { 264 case RELEASE_5, RELEASE_6, 265 RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, 266 Location.METHOD, 267 Location.INNER_CLASS); 268 case RELEASE_8 -> Set.of(Location.CLASS, Location.FIELD, 269 Location.METHOD, 270 Location.INNER_CLASS, 271 Location.METHOD_PARAMETER); 272 default -> SYNTHETIC.locations(); 273 }; 274 } 275 compareLocations(expected, SYNTHETIC, cffv); 276 } 277 } 278 279 private static void testStrict() { 280 for (var cffv : ClassFileFormatVersion.values()) { 281 Set<AccessFlag.Location> expected = 282 (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && 283 cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? 284 Set.of(Location.METHOD) : 285 Set.of(); 286 compareLocations(expected, STRICT, cffv); 287 } 288 } 289 290 private static void testLatestMatch() { 291 // Verify accessFlag.locations() and 292 // accessFlag.locations(ClassFileFormatVersion.latest()) are 293 // consistent 294 var LATEST = ClassFileFormatVersion.latest(); 295 for (var accessFlag : AccessFlag.values()) { 296 var locationSet = accessFlag.locations(); 297 var locationLatestSet = accessFlag.locations(LATEST); 298 if (!locationSet.equals(locationLatestSet)) { 299 throw new RuntimeException("Unequal location sets for " + accessFlag); 300 } 301 } 302 } 303 304 private static void testFlagVersionConsistency() { 305 for (var flag : AccessFlag.values()) { 306 for (var location : AccessFlag.Location.values()) { 307 if (location.flags().contains(flag) != flag.locations().contains(location)) { 308 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + 309 "flag %s and location %s are inconsistent for the latest version", flag, location)); 310 } 311 } 312 } 313 for (var cffv : ClassFileFormatVersion.values()) { 314 for (var flag : AccessFlag.values()) { 315 for (var location : AccessFlag.Location.values()) { 316 if (location.flags(cffv).contains(flag) != flag.locations(cffv).contains(location)) { 317 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + 318 "flag %s and location %s are inconsistent for class file version %s", flag, location, cffv)); 319 } 320 } 321 } 322 } 323 } 324 325 private static void testLocationMaskFlagConsistency() { 326 for (var location : AccessFlag.Location.values()) { 327 if (!flagsAndMaskMatch(location.flags(), location.flagsMask())) { 328 throw new RuntimeException(String.format("Flags and mask mismatch for %s", location)); 329 } 330 for (var cffv : ClassFileFormatVersion.values()) { 331 if (!flagsAndMaskMatch(location.flags(cffv), location.flagsMask(cffv))) { 332 throw new RuntimeException(String.format("Flags and mask mismatch for %s in %s", location, cffv)); 333 } 334 } 335 } 336 } 337 338 private static boolean flagsAndMaskMatch(Set<AccessFlag> flags, int mask) { 339 for (var flag : flags) { 340 int bit = flag.mask(); 341 if (((mask & bit) == 0)) 342 return false; 343 mask &= ~bit; 344 } 345 return mask == 0; 346 } 347 }