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 }