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