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