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 }