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