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 }