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