jdk-24/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java
Joe Darcy 6fd1442123 8296743: Tighten Class.getModifiers spec for array classes
Reviewed-by: rriggs, mchung, heidinga
2022-11-18 21:51:55 +00:00

219 lines
7.3 KiB
Java

/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8266670 8291734 8296743
* @summary Test expected AccessFlag's on classes.
*/
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
/*
* Class access flags that can directly or indirectly declared in
* source include:
* public, private, protected, static, final, interface, abstract,
* annotation, enum.
*
* Additionally, the access flags super and synthetic cannot be
* explicitly applied.
*
* This test is written on top of the facilities of core reflection.
*
* Note that core reflection does not offer a supported mechanism to
* return the Class object created from a module-info.class
* file. Therefore, this test does not attempt to probe the setting of
* that access flag.
*/
@ExpectedClassFlags("[PUBLIC, FINAL, SUPER]")
public final class ClassAccessFlagTest {
public static void main(String... args) {
// Top-level and auxiliary classes; i.e. non-inner classes
Class<?>[] testClasses = {
ClassAccessFlagTest.class,
TestInterface.class,
ExpectedClassFlags.class,
TestOuterEnum.class
};
checkClasses(testClasses);
// Nested classes of ClassAccessFlagTest
checkClasses(ClassAccessFlagTest.class.getDeclaredClasses());
checkPrimitives();
checkArrays();
}
private static void checkClasses(Class<?>[] classes) {
for (var clazz : classes) {
checkClass(clazz);
}
}
private static void checkClass(Class<?> clazz) {
ExpectedClassFlags expected =
clazz.getAnnotation(ExpectedClassFlags.class);
if (expected != null) {
String actual = clazz.accessFlags().toString();
if (!expected.value().equals(actual)) {
throw new RuntimeException("On " + clazz +
" expected " + expected.value() +
" got " + actual);
}
}
}
private static void checkPrimitives() {
final Class<?>[] primitives = {
byte.class,
int.class,
long.class,
short.class,
char.class,
float.class,
double.class,
boolean.class,
void.class // same access flag rules
};
var expected = Set.of(AccessFlag.PUBLIC,
AccessFlag.FINAL,
AccessFlag.ABSTRACT);
for(var primClass : primitives) {
var accessFlags = primClass.accessFlags();
if (!accessFlags.equals(expected)) {
throw new RuntimeException("Unexpected flags on " +
primClass);
}
}
}
private static boolean containsAny(Set<AccessFlag> input,
Set<AccessFlag> test) {
var copy = new HashSet<>(input);
return copy.removeAll(test);
}
private static void checkArrays() {
Class<?>[] accessClasses = {
PublicInterface.class,
ProtectedInterface.class,
PrivateInterface.class,
};
for (var accessClass : accessClasses) {
AccessFlag accessLevel;
var flags = accessClass.accessFlags();
if (flags.contains(AccessFlag.PUBLIC))
accessLevel = AccessFlag.PUBLIC;
else if (flags.contains(AccessFlag.PROTECTED))
accessLevel = AccessFlag.PROTECTED;
else if (flags.contains(AccessFlag.PRIVATE))
accessLevel = AccessFlag.PRIVATE;
else
accessLevel = null;
var arrayClass = accessClass.arrayType();
// Access modifier must match on the array type
if (accessLevel != null) {
if (!arrayClass.accessFlags().contains(accessLevel)) {
throw new RuntimeException("Mismatched access flags on " +
arrayClass);
}
} else {
if (containsAny(arrayClass.accessFlags(),
Set.of(AccessFlag.PUBLIC,
AccessFlag.PROTECTED,
AccessFlag.PRIVATE))) {
throw new RuntimeException("Unexpected access flags on " +
arrayClass);
}
}
}
}
// inner classes and interfaces; possible flags on INNER_CLASS
// locations:
// PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT,
// SYNTHETIC, ANNOTATION, ENUM.
@ExpectedClassFlags("[PUBLIC, STATIC, INTERFACE, ABSTRACT]")
public interface PublicInterface {}
@ExpectedClassFlags("[PROTECTED, STATIC, INTERFACE, ABSTRACT]")
protected interface ProtectedInterface {}
@ExpectedClassFlags("[PRIVATE, STATIC, INTERFACE, ABSTRACT]")
private interface PrivateInterface {}
@ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
/*package*/ interface PackageInterface {}
@ExpectedClassFlags("[FINAL]")
/*package*/ final class TestFinalClass {}
@ExpectedClassFlags("[ABSTRACT]")
/*package*/ abstract class TestAbstractClass {}
@ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT, ANNOTATION]")
/*package*/ @interface TestMarkerAnnotation {}
@ExpectedClassFlags("[PUBLIC, STATIC, FINAL, ENUM]")
public enum MetaSynVar {
QUUX;
}
// Is there is at least one special enum constant, the enum class
// itself is implicitly abstract rather than final.
@ExpectedClassFlags("[PROTECTED, STATIC, ABSTRACT, ENUM]")
protected enum MetaSynVar2 {
WOMBAT{
@Override
public int foo() {return 42;}
};
public abstract int foo();
}
@ExpectedClassFlags("[PRIVATE, ABSTRACT]")
private abstract class Foo {}
@ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
interface StaticTestInterface {}
}
@Retention(RetentionPolicy.RUNTIME)
@ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]")
@interface ExpectedClassFlags {
String value();
}
@ExpectedClassFlags("[INTERFACE, ABSTRACT]")
interface TestInterface {}
@ExpectedClassFlags("[FINAL, SUPER, ENUM]")
enum TestOuterEnum {
INSTANCE;
}