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 }