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