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.RELEASE_22), 124 125 new StepFunctionTC(OPEN, 126 Set.of(), 127 ClassFileFormatVersion.RELEASE_9), 128 129 new StepFunctionTC(TRANSITIVE, 130 Set.of(), 131 ClassFileFormatVersion.RELEASE_9), 132 133 new StepFunctionTC(STATIC_PHASE, 134 Set.of(), 135 ClassFileFormatVersion.RELEASE_9), 136 137 new StepFunctionTC(BRIDGE, 138 Set.of(), 139 ClassFileFormatVersion.RELEASE_5), 140 141 new StepFunctionTC(VARARGS, 142 Set.of(), 143 ClassFileFormatVersion.RELEASE_5), 144 145 new StepFunctionTC(INTERFACE, 146 removeInnerClass(INTERFACE.locations()), 147 ClassFileFormatVersion.RELEASE_1), 148 149 new StepFunctionTC(ABSTRACT, 150 removeInnerClass(ABSTRACT.locations()), 151 ClassFileFormatVersion.RELEASE_1), 152 153 new StepFunctionTC(ANNOTATION, 154 Set.of(), 155 ClassFileFormatVersion.RELEASE_5), 156 157 new StepFunctionTC(ENUM, 158 Set.of(), 159 ClassFileFormatVersion.RELEASE_5), 160 161 new StepFunctionTC(MODULE, 162 Set.of(), 163 ClassFileFormatVersion.RELEASE_9) 164 }; 165 166 for (var testCase : testCases) { 167 for (var cffv : ClassFileFormatVersion.values()) { 168 compareLocations(cffv.compareTo(testCase.transition()) >= 0 ? 169 testCase.finalLocs() : 170 testCase.initialLocs(), 171 testCase.accessFlag, cffv); 172 } 173 } 174 } 175 176 private static void compareLocations(Set<AccessFlag.Location> expected, 177 AccessFlag accessFlag, 178 ClassFileFormatVersion cffv) { 179 var actual = accessFlag.locations(cffv); 180 if (!expected.equals(actual)) { 181 throw new RuntimeException("Unexpected locations for " + 182 accessFlag + " on " + cffv + "\n" + 183 "Expected " + expected + "; got \t" + actual); 184 } 185 } 186 187 private static Set<AccessFlag.Location> removeInnerClass(Set<AccessFlag.Location> locations) { 188 var s = new HashSet<>(locations); 189 s.remove(Location.INNER_CLASS); 190 return s; 191 } 192 193 private record StepFunctionTC(AccessFlag accessFlag, 194 Set<AccessFlag.Location> initialLocs, 195 ClassFileFormatVersion transition) { 196 197 public Set<AccessFlag.Location> finalLocs() { 198 return accessFlag.locations(); 199 } 200 } 201 202 203 private record TwoStepFunctionTC(AccessFlag accessFlag, 204 Set<AccessFlag.Location> initialLocs, 205 ClassFileFormatVersion transition1, 206 Set<AccessFlag.Location> firstLocs, 207 ClassFileFormatVersion transition2) { 208 209 public Set<AccessFlag.Location> secondLocs() { 210 return accessFlag.locations(); 211 } 212 } 213 214 private static void testTwoStepAccessFlags() { 215 TwoStepFunctionTC[] testCases = { 216 new TwoStepFunctionTC(FINAL, 217 Set.of(Location.CLASS, Location.FIELD, Location.METHOD), 218 ClassFileFormatVersion.RELEASE_1, 219 Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS), 220 ClassFileFormatVersion.RELEASE_8), 221 222 new TwoStepFunctionTC(MANDATED, 223 Set.of(), 224 ClassFileFormatVersion.RELEASE_8, 225 Set.of(Location.METHOD_PARAMETER), 226 ClassFileFormatVersion.RELEASE_9), 227 }; 228 229 for (var testCase : testCases) { 230 for (var cffv : ClassFileFormatVersion.values()) { 231 var transition1 = testCase.transition1(); 232 var transition2 = testCase.transition2(); 233 Set<AccessFlag.Location> expected; 234 if (cffv.compareTo(transition1) < 0) { 235 expected = testCase.initialLocs(); 236 } else if (cffv.compareTo(transition1) >= 0 && 237 cffv.compareTo(transition2) < 0) { 238 expected = testCase.firstLocs(); 239 } else { // cffv >= transition2 240 expected = testCase.secondLocs(); 241 } 242 243 compareLocations(expected, testCase.accessFlag(), cffv); 244 } 245 } 246 } 247 248 private static void testSynthetic() { 249 for (var cffv : ClassFileFormatVersion.values()) { 250 Set<AccessFlag.Location> expected; 251 if (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) < 0) { 252 expected = Set.of(); 253 } else { 254 expected = 255 switch(cffv) { 256 case RELEASE_5, RELEASE_6, 257 RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, 258 Location.METHOD, 259 Location.INNER_CLASS); 260 case RELEASE_8 -> Set.of(Location.CLASS, Location.FIELD, 261 Location.METHOD, 262 Location.INNER_CLASS, 263 Location.METHOD_PARAMETER); 264 default -> SYNTHETIC.locations(); 265 }; 266 } 267 compareLocations(expected, SYNTHETIC, cffv); 268 } 269 } 270 271 private static void testStrict() { 272 for (var cffv : ClassFileFormatVersion.values()) { 273 Set<AccessFlag.Location> expected = 274 (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && 275 cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? 276 Set.of(Location.METHOD) : 277 Set.of(); 278 compareLocations(expected, STRICT, cffv); 279 } 280 } 281 282 private static void testLatestMatch() { 283 // Verify accessFlag.locations() and 284 // accessFlag.locations(ClassFileFormatVersion.latest()) are 285 // consistent 286 var LATEST = ClassFileFormatVersion.latest(); 287 for (var accessFlag : AccessFlag.values()) { 288 var locationSet = accessFlag.locations(); 289 var locationLatestSet = accessFlag.locations(LATEST); 290 if (!locationSet.equals(locationLatestSet)) { 291 throw new RuntimeException("Unequal location sets for " + accessFlag); 292 } 293 } 294 } 295 296 private static void testFlagVersionConsistency() { 297 for (var flag : AccessFlag.values()) { 298 for (var location : AccessFlag.Location.values()) { 299 if (location.flags().contains(flag) != flag.locations().contains(location)) { 300 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + 301 "flag %s and location %s are inconsistent for the latest version", flag, location)); 302 } 303 } 304 } 305 for (var cffv : ClassFileFormatVersion.values()) { 306 for (var flag : AccessFlag.values()) { 307 for (var location : AccessFlag.Location.values()) { 308 if (location.flags(cffv).contains(flag) != flag.locations(cffv).contains(location)) { 309 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + 310 "flag %s and location %s are inconsistent for class file version %s", flag, location, cffv)); 311 } 312 } 313 } 314 } 315 } 316 317 private static void testLocationMaskFlagConsistency() { 318 for (var location : AccessFlag.Location.values()) { 319 if (!flagsAndMaskMatch(location.flags(), location.flagsMask())) { 320 throw new RuntimeException(String.format("Flags and mask mismatch for %s", location)); 321 } 322 for (var cffv : ClassFileFormatVersion.values()) { 323 if (!flagsAndMaskMatch(location.flags(cffv), location.flagsMask(cffv))) { 324 throw new RuntimeException(String.format("Flags and mask mismatch for %s in %s", location, cffv)); 325 } 326 } 327 } 328 } 329 330 private static boolean flagsAndMaskMatch(Set<AccessFlag> flags, int mask) { 331 for (var flag : flags) { 332 int bit = flag.mask(); 333 if (((mask & bit) == 0)) 334 return false; 335 mask &= ~bit; 336 } 337 return mask == 0; 338 } 339 }