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 step 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(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(SUPER, 119 Set.of(AccessFlag.Location.CLASS), 120 ClassFileFormatVersion.RELEASE_22), 121 122 new StepFunctionTC(OPEN, 123 Set.of(), 124 ClassFileFormatVersion.RELEASE_9), 125 126 new StepFunctionTC(TRANSITIVE, 127 Set.of(), 128 ClassFileFormatVersion.RELEASE_9), 129 130 new StepFunctionTC(STATIC_PHASE, 131 Set.of(), 132 ClassFileFormatVersion.RELEASE_9), 133 134 new StepFunctionTC(BRIDGE, 135 Set.of(), 136 ClassFileFormatVersion.RELEASE_5), 137 138 new StepFunctionTC(VARARGS, 139 Set.of(), 140 ClassFileFormatVersion.RELEASE_5), 141 142 new StepFunctionTC(INTERFACE, 143 removeInnerClass(INTERFACE.locations()), 144 ClassFileFormatVersion.RELEASE_1), 145 146 new StepFunctionTC(ABSTRACT, 147 removeInnerClass(ABSTRACT.locations()), 148 ClassFileFormatVersion.RELEASE_1), 149 150 new StepFunctionTC(ANNOTATION, 151 Set.of(), 152 ClassFileFormatVersion.RELEASE_5), 153 154 new StepFunctionTC(ENUM, 155 Set.of(), 156 ClassFileFormatVersion.RELEASE_5), 157 158 new StepFunctionTC(MODULE, 159 Set.of(), 160 ClassFileFormatVersion.RELEASE_9) 161 }; 162 163 for (var testCase : testCases) { 164 for (var cffv : ClassFileFormatVersion.values()) { 165 compareLocations(cffv.compareTo(testCase.transition()) >= 0 ? 166 testCase.finalLocs() : 167 testCase.initialLocs(), 168 testCase.accessFlag, cffv); 169 } 170 } 171 } 172 173 private static void compareLocations(Set<AccessFlag.Location> expected, 174 AccessFlag accessFlag, 175 ClassFileFormatVersion cffv) { 176 var actual = accessFlag.locations(cffv); 177 if (!expected.equals(actual)) { 178 throw new RuntimeException("Unexpected locations for " + 179 accessFlag + " on " + cffv + "\n" + 180 "Expected " + expected + "; got \t" + actual); 181 } 182 } 183 184 private static Set<AccessFlag.Location> removeInnerClass(Set<AccessFlag.Location> locations) { 185 var s = new HashSet<>(locations); 186 s.remove(Location.INNER_CLASS); 187 return s; 188 } 189 190 private record StepFunctionTC(AccessFlag accessFlag, 191 Set<AccessFlag.Location> initialLocs, 192 ClassFileFormatVersion transition) { 193 194 public Set<AccessFlag.Location> finalLocs() { 195 return accessFlag.locations(); 196 } 197 } 198 199 200 private record TwoStepFunctionTC(AccessFlag accessFlag, 201 Set<AccessFlag.Location> initialLocs, 202 ClassFileFormatVersion transition1, 203 Set<AccessFlag.Location> firstLocs, 204 ClassFileFormatVersion transition2) { 205 206 public Set<AccessFlag.Location> secondLocs() { 207 return accessFlag.locations(); 208 } 209 } 210 211 private static void testTwoStepAccessFlags() { 212 TwoStepFunctionTC[] testCases = { 213 new TwoStepFunctionTC(FINAL, 214 Set.of(Location.CLASS, Location.FIELD, Location.METHOD), 215 ClassFileFormatVersion.RELEASE_1, 216 Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS), 217 ClassFileFormatVersion.RELEASE_8), 218 219 new TwoStepFunctionTC(MANDATED, 220 Set.of(), 221 ClassFileFormatVersion.RELEASE_8, 222 Set.of(Location.METHOD_PARAMETER), 223 ClassFileFormatVersion.RELEASE_9), 224 }; 225 226 for (var testCase : testCases) { 227 for (var cffv : ClassFileFormatVersion.values()) { 228 var transition1 = testCase.transition1(); 229 var transition2 = testCase.transition2(); 230 Set<AccessFlag.Location> expected; 231 if (cffv.compareTo(transition1) < 0) { 232 expected = testCase.initialLocs(); 233 } else if (cffv.compareTo(transition1) >= 0 && 234 cffv.compareTo(transition2) < 0) { 235 expected = testCase.firstLocs(); 236 } else { // cffv >= transition2 237 expected = testCase.secondLocs(); 238 } 239 240 compareLocations(expected, testCase.accessFlag(), cffv); 241 } 242 } 243 } 244 245 private static void testSynthetic() { 246 for (var cffv : ClassFileFormatVersion.values()) { 247 Set<AccessFlag.Location> expected; 248 if (cffv.compareTo(ClassFileFormatVersion.RELEASE_6) <= 0) { 249 expected = Set.of(); 250 } else { 251 expected = 252 switch(cffv) { 253 case RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, 254 Location.METHOD, 255 Location.INNER_CLASS); 256 case RELEASE_8 -> Set.of(Location.CLASS, Location.FIELD, 257 Location.METHOD, 258 Location.INNER_CLASS, 259 Location.METHOD_PARAMETER); 260 default -> SYNTHETIC.locations(); 261 }; 262 } 263 compareLocations(expected, SYNTHETIC, cffv); 264 } 265 } 266 267 private static void testStrict() { 268 for (var cffv : ClassFileFormatVersion.values()) { 269 Set<AccessFlag.Location> expected = 270 (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && 271 cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? 272 Set.of(Location.METHOD) : 273 Set.of(); 274 compareLocations(expected, STRICT, cffv); 275 } 276 } 277 278 private static void testLatestMatch() { 279 // Verify accessFlag.locations() and 280 // accessFlag.locations(ClassFileFormatVersion.latest()) are 281 // consistent 282 var LATEST = ClassFileFormatVersion.latest(); 283 for (var accessFlag : AccessFlag.values()) { 284 var locationSet = accessFlag.locations(); 285 var locationLatestSet = accessFlag.locations(LATEST); 286 if (!locationSet.equals(locationLatestSet)) { 287 throw new RuntimeException("Unequal location sets for " + accessFlag); 288 } 289 } 290 } 291 292 }