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 }