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 }