1 /*
  2  * Copyright (c) 2022, 2025, 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 8266670 8291734 8296743 8294866
 27  * @summary Test expected AccessFlag's on classes.
 28  * @modules java.base/jdk.internal.misc
 29  * @enablePreview false
 30  * @run main ClassAccessFlagPreviewTest
 31  * @enablePreview true
 32  * @compile -XDforcePreview ClassAccessFlagPreviewTest.java
 33  * @run main ClassAccessFlagPreviewTest
 34  */
 35 
 36 import java.lang.annotation.*;
 37 import java.lang.reflect.*;
 38 import java.util.*;
 39 
 40 import jdk.internal.misc.PreviewFeatures;
 41 
 42 /*
 43  * Class access flags that can directly or indirectly declared in
 44  * source include:
 45  * public, private, protected, static, final, interface, abstract,
 46  * annotation, enum.
 47  *
 48  * Additionally, the access flags super and synthetic cannot be
 49  * explicitly applied.
 50  *
 51  * This test is written on top of the facilities of core reflection.
 52  *
 53  * Note that core reflection does not offer a supported mechanism to
 54  * return the Class object created from a module-info.class
 55  * file. Therefore, this test does not attempt to probe the setting of
 56  * that access flag.
 57  */
 58 @ExpectedClassFlags(value = "[PUBLIC, FINAL, SUPER]",
 59         preview = "[PUBLIC, FINAL, IDENTITY]")
 60 public final class ClassAccessFlagPreviewTest {
 61     public static void main(String... args) {
 62         // Top-level and auxiliary classes; i.e. non-inner classes
 63         Class<?>[] testClasses = {
 64             ClassAccessFlagPreviewTest.class,
 65             TestInterface.class,
 66             TestFinalClass.class,
 67             TestAbstractClass.class,
 68             TestAbstractValueClass.class,
 69             TestPrivateAbstractClass.class,
 70             TestPrivateAbstractValueClass.class,
 71             StaticTestInterface.class,
 72             TestMarkerAnnotation.class,
 73             ExpectedClassFlags.class,
 74             TestOuterEnum.class
 75         };
 76         checkClasses(testClasses);
 77 
 78         // Nested classes of ClassAccessFlagPreviewTest
 79         checkClasses(ClassAccessFlagPreviewTest.class.getDeclaredClasses());
 80 
 81         checkPrimitives();
 82         checkArrays();
 83     }
 84 
 85     private static void checkClasses(Class<?>[] classes) {
 86         for (var clazz : classes) {
 87             checkClass(clazz);
 88         }
 89     }
 90 
 91     private static void checkClass(Class<?> clazz) {
 92         ExpectedClassFlags expected =
 93             clazz.getAnnotation(ExpectedClassFlags.class);
 94         if (expected != null) {
 95             String actual = clazz.accessFlags().toString();
 96             String expectedFlags = (PreviewFeatures.isEnabled() && !expected.preview().isEmpty())
 97                     ? expected.preview() : expected.value();
 98             if (!expectedFlags.equals(actual)) {
 99                 throw new RuntimeException("On " + clazz +
100                                            " expected " + expected +
101                                            " got " + actual);
102             }
103         }
104     }
105 
106     private static void checkPrimitives() {
107         final Class<?>[] primitives = {
108             byte.class,
109             int.class,
110             long.class,
111             short.class,
112             char.class,
113             float.class,
114             double.class,
115             boolean.class,
116             void.class // same access flag rules
117         };
118 
119         var expected = Set.of(AccessFlag.PUBLIC,
120                               AccessFlag.FINAL,
121                               AccessFlag.ABSTRACT);
122 
123         for(var primClass : primitives) {
124             var accessFlags = primClass.accessFlags();
125             if (!accessFlags.equals(expected)) {
126                 throw new RuntimeException("Unexpected flags on " +
127                                            primClass);
128             }
129         }
130     }
131 
132     private static boolean containsAny(Set<AccessFlag> input,
133                                        Set<AccessFlag> test) {
134         var copy = new HashSet<>(input);
135         return copy.removeAll(test);
136     }
137 
138     private static void checkArrays() {
139         Class<?>[] accessClasses = {
140             PublicInterface.class,
141             ProtectedInterface.class,
142             PrivateInterface.class,
143         };
144 
145         for (var accessClass : accessClasses) {
146             AccessFlag accessLevel;
147             var flags = accessClass.accessFlags();
148             if (flags.contains(AccessFlag.PUBLIC))
149                 accessLevel = AccessFlag.PUBLIC;
150             else if (flags.contains(AccessFlag.PROTECTED))
151                 accessLevel = AccessFlag.PROTECTED;
152             else if (flags.contains(AccessFlag.PRIVATE))
153                 accessLevel = AccessFlag.PRIVATE;
154             else
155                 accessLevel = null;
156 
157             var arrayClass = accessClass.arrayType();
158             // Access modifier must match on the array type
159             if (accessLevel != null) {
160                 if (!arrayClass.accessFlags().contains(accessLevel)) {
161                     throw new RuntimeException("Mismatched access flags on " +
162                                                arrayClass);
163                 }
164             } else {
165                 if (containsAny(arrayClass.accessFlags(),
166                                 Set.of(AccessFlag.PUBLIC,
167                                        AccessFlag.PROTECTED,
168                                        AccessFlag.PRIVATE))) {
169                     throw new RuntimeException("Unexpected access flags on " +
170                                                arrayClass);
171                 }
172             }
173             // Verify IDENTITY, ABSTRACT, FINAL, and access mode
174             Set<AccessFlag> expected = new HashSet<>(4);
175             expected.add(AccessFlag.ABSTRACT);
176             expected.add(AccessFlag.FINAL);
177             expected.add(AccessFlag.IDENTITY);
178             if (accessLevel != null)
179                 expected.add(accessLevel);
180             if (!expected.equals(arrayClass.accessFlags())) {
181                 throw new RuntimeException("Unexpected access flags for array: " + accessClass +
182                         ": actual: " + arrayClass.accessFlags() +
183                         ", expected: " + expected);
184             }
185         }
186 
187     }
188 
189     // inner classes and interfaces; possible flags on INNER_CLASS
190     // locations:
191     // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT,
192     // SYNTHETIC, ANNOTATION, ENUM.
193     // Include cases for classes with identity, value modifier, or no modifier.
194 
195     @ExpectedClassFlags("[PUBLIC, STATIC, INTERFACE, ABSTRACT]")
196     public      interface PublicInterface {}
197     @ExpectedClassFlags("[PROTECTED, STATIC, INTERFACE, ABSTRACT]")
198     protected   interface ProtectedInterface {}
199     @ExpectedClassFlags("[PRIVATE, STATIC, INTERFACE, ABSTRACT]")
200     private     interface PrivateInterface {}
201     @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
202     /*package*/ interface PackageInterface {}
203 
204     @ExpectedClassFlags(value = "[FINAL]",
205             preview = "[FINAL, IDENTITY]")
206     /*package*/ final class TestFinalClass {}
207 
208     @ExpectedClassFlags(value = "[ABSTRACT]",
209             preview = "[IDENTITY, ABSTRACT]")
210     /*package*/ abstract class TestAbstractClass {}
211 
212     @ExpectedClassFlags(value = "[ABSTRACT]",
213             preview = "[ABSTRACT]")
214     /*package*/ abstract value class TestAbstractValueClass {}
215 
216     @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT, ANNOTATION]")
217     /*package*/ @interface TestMarkerAnnotation {}
218 
219     @ExpectedClassFlags(value = "[PUBLIC, STATIC, FINAL, ENUM]",
220             preview = "[PUBLIC, STATIC, FINAL, IDENTITY, ENUM]")
221     public enum MetaSynVar {
222         QUUX;
223     }
224 
225     // Is there is at least one special enum constant, the enum class
226     // itself is implicitly abstract rather than final.
227     @ExpectedClassFlags(value = "[PROTECTED, STATIC, ABSTRACT, ENUM]",
228             preview = "[PROTECTED, STATIC, IDENTITY, ABSTRACT, ENUM]")
229     protected enum MetaSynVar2 {
230         WOMBAT{
231             @Override
232             public int foo() {return 42;}
233         };
234         public abstract int foo();
235     }
236 
237     @ExpectedClassFlags(value = "[PRIVATE, ABSTRACT]",
238             preview = "[PRIVATE, IDENTITY, ABSTRACT]")
239     private abstract class TestPrivateAbstractClass {}
240 
241     @ExpectedClassFlags(value = "[PRIVATE, ABSTRACT]",
242             preview = "[PRIVATE, ABSTRACT]")
243     private abstract value class TestPrivateAbstractValueClass {}
244 
245     @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
246     interface StaticTestInterface {}
247 }
248 
249 @Retention(RetentionPolicy.RUNTIME)
250 @ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]")
251 @interface ExpectedClassFlags {
252     String value();
253     String preview() default "";
254 }
255 
256 @ExpectedClassFlags("[INTERFACE, ABSTRACT]")
257 interface TestInterface {}
258 
259 
260 @ExpectedClassFlags(value="[FINAL, SUPER, ENUM]",
261         preview="[FINAL, IDENTITY, ENUM]")
262 enum TestOuterEnum {
263     INSTANCE;
264 }