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