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 invariant
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(SUPER, 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(OPEN,
122 Set.of(),
123 ClassFileFormatVersion.RELEASE_9),
124
125 new StepFunctionTC(TRANSITIVE,
126 Set.of(),
127 ClassFileFormatVersion.RELEASE_9),
128
129 new StepFunctionTC(STATIC_PHASE,
130 Set.of(),
131 ClassFileFormatVersion.RELEASE_9),
132
133 new StepFunctionTC(BRIDGE,
134 Set.of(),
135 ClassFileFormatVersion.RELEASE_5),
136
137 new StepFunctionTC(VARARGS,
138 Set.of(),
139 ClassFileFormatVersion.RELEASE_5),
140
174 ClassFileFormatVersion cffv) {
175 var actual = accessFlag.locations(cffv);
176 if (!expected.equals(actual)) {
177 throw new RuntimeException("Unexpected locations for " +
178 accessFlag + " on " + cffv + "\n" +
179 "Expected " + expected + "; got \t" + actual);
180 }
181 }
182
183 private static Set<AccessFlag.Location> removeInnerClass(Set<AccessFlag.Location> locations) {
184 var s = new HashSet<>(locations);
185 s.remove(Location.INNER_CLASS);
186 return s;
187 }
188
189 private record StepFunctionTC(AccessFlag accessFlag,
190 Set<AccessFlag.Location> initialLocs,
191 ClassFileFormatVersion transition) {
192
193 public Set<AccessFlag.Location> finalLocs() {
194 return accessFlag.locations();
195 }
196 }
197
198
199 private record TwoStepFunctionTC(AccessFlag accessFlag,
200 Set<AccessFlag.Location> initialLocs,
201 ClassFileFormatVersion transition1,
202 Set<AccessFlag.Location> firstLocs,
203 ClassFileFormatVersion transition2) {
204
205 public Set<AccessFlag.Location> secondLocs() {
206 return accessFlag.locations();
207 }
208 }
209
210 private static void testTwoStepAccessFlags() {
211 TwoStepFunctionTC[] testCases = {
212 new TwoStepFunctionTC(FINAL,
213 Set.of(Location.CLASS, Location.FIELD, Location.METHOD),
214 ClassFileFormatVersion.RELEASE_1,
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 private static void testFlagVersionConsistency() {
293 for (var flag : AccessFlag.values()) {
294 for (var location : AccessFlag.Location.values()) {
295 if (location.flags().contains(flag) != flag.locations().contains(location)) {
296 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" +
297 "flag %s and location %s are inconsistent for the latest version"));
298 }
299 }
300 }
301 for (var cffv : ClassFileFormatVersion.values()) {
302 for (var flag : AccessFlag.values()) {
303 for (var location : AccessFlag.Location.values()) {
304 if (location.flags(cffv).contains(flag) != flag.locations(cffv).contains(location)) {
305 throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" +
306 "flag %s and location %s are inconsistent for class file version %s"));
307 }
308 }
309 }
310 }
311 }
312
313 private static void testLocationMaskFlagConsistency() {
314 for (var location : AccessFlag.Location.values()) {
315 if (!flagsAndMaskMatch(location.flags(), location.flagsMask())) {
316 throw new RuntimeException(String.format("Flags and mask mismatch for %s", location));
317 }
318 for (var cffv : ClassFileFormatVersion.values()) {
319 if (!flagsAndMaskMatch(location.flags(cffv), location.flagsMask(cffv))) {
320 throw new RuntimeException(String.format("Flags and mask mismatch for %s in %s", location, cffv));
321 }
322 }
323 }
324 }
325
326 private static boolean flagsAndMaskMatch(Set<AccessFlag> flags, int mask) {
|
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
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,
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) {
|