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