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