From 6fd1442123f7c67740280e8555f60f01a4a2a30e Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Fri, 18 Nov 2022 21:51:55 +0000 Subject: [PATCH] 8296743: Tighten Class.getModifiers spec for array classes Reviewed-by: rriggs, mchung, heidinga --- .../share/classes/java/lang/Class.java | 51 ++++--- .../TestPrimitiveAndArrayModifiers.java | 136 ++++++++++++++++++ .../AccessFlag/ClassAccessFlagTest.java | 21 +-- 3 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 test/jdk/java/lang/Class/getModifiers/TestPrimitiveAndArrayModifiers.java diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 116a0a65c96..9e59f3438e9 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -1296,17 +1296,21 @@ public final class Class implements java.io.Serializable, * {@code abstract} and {@code interface}; they should be decoded * using the methods of class {@code Modifier}. * - *

If the underlying class is an array class, then its - * {@code public}, {@code private} and {@code protected} - * modifiers are the same as those of its component type. If this - * {@code Class} object represents a primitive type or void, its - * {@code public} modifier is always {@code true}, and its - * {@code protected} and {@code private} modifiers are always - * {@code false}. If this {@code Class} object represents an array class, a - * primitive type or void, then its {@code final} modifier is always - * {@code true} and its interface modifier is always - * {@code false}. The values of its other modifiers are not determined - * by this specification. + *

If the underlying class is an array class: + *

+ * If this {@code Class} object represents a primitive type or + * void, its {@code public}, {@code abstract}, and {@code final} + * modifiers are always {@code true}. + * For {@code Class} objects representing void, primitive types, and + * arrays, the values of other modifiers are {@code false} other + * than as specified above. * *

The modifier encodings are defined in section {@jvms 4.1} * of The Java Virtual Machine Specification. @@ -1320,6 +1324,7 @@ public final class Class implements java.io.Serializable, * @since 1.1 * @jls 8.1.1 Class Modifiers * @jls 9.1.1. Interface Modifiers + * @jvms 4.1 The {@code ClassFile} Structure */ @IntrinsicCandidate public native int getModifiers(); @@ -1328,17 +1333,19 @@ public final class Class implements java.io.Serializable, * {@return an unmodifiable set of the {@linkplain AccessFlag access * flags} for this class, possibly empty} * - *

If the underlying class is an array class, then its - * {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED} - * access flags are the same as those of its component type. If this - * {@code Class} object represents a primitive type or void, the - * {@code PUBLIC} access flag is present, and the - * {@code PROTECTED} and {@code PRIVATE} access flags are always - * absent. If this {@code Class} object represents an array class, a - * primitive type or void, then the {@code FINAL} access flag is always - * present and the interface access flag is always - * absent. The values of its other access flags are not determined - * by this specification. + *

If the underlying class is an array class: + *

+ * If this {@code Class} object represents a primitive type or + * void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and + * {@code FINAL}. + * For {@code Class} objects representing void, primitive types, and + * arrays, access flags are absent other than as specified above. * * @see #getModifiers() * @jvms 4.1 The ClassFile Structure diff --git a/test/jdk/java/lang/Class/getModifiers/TestPrimitiveAndArrayModifiers.java b/test/jdk/java/lang/Class/getModifiers/TestPrimitiveAndArrayModifiers.java new file mode 100644 index 00000000000..5efa0b3f0d8 --- /dev/null +++ b/test/jdk/java/lang/Class/getModifiers/TestPrimitiveAndArrayModifiers.java @@ -0,0 +1,136 @@ +/* + * 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. + */ + +import java.lang.reflect.Modifier; +import java.lang.annotation.*; + +/* + * @test + * @bug 8296743 + * @summary Verify array classes and primitives have expected modifiers + */ +@ExpectedModifiers(Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT) +public class TestPrimitiveAndArrayModifiers { + + /* + * Relevant excerpt of the Class.getModifiers() specification: + *

If the underlying class is an array class: + *

+ */ + + public static void main(String... args) throws Exception { + testPrimitives(); + testArrays(); + } + + private static void testArrays() { + Class[] testCases = { + TestPrimitiveAndArrayModifiers.class, + + PackagePrivateClass.class, + ProtectedClass.class, + PrivateClass.class, + + PublicInterface.class, + PackagePrivateInterface.class, + ProtectedInterface.class, + PrivateInterface.class, + }; + + for(var testCase : testCases) { + int expectedModifiers = + testCase.getAnnotation(ExpectedModifiers.class).value(); + Class arrayClass = testCase.arrayType(); + int actualModifiers = arrayClass.getModifiers(); + if (expectedModifiers != actualModifiers) { + throw new RuntimeException("Expected " + Modifier.toString(expectedModifiers) + + "on " + testCase.getCanonicalName() + + ", but got " + Modifier.toString(actualModifiers)); + } + } + } + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT) + class PackagePrivateClass {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT | Modifier.PROTECTED) + protected class ProtectedClass {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT | Modifier.PRIVATE) + private class PrivateClass {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT | Modifier.PUBLIC) + public interface PublicInterface {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT) + interface PackagePrivateInterface {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT | Modifier.PROTECTED) + protected interface ProtectedInterface {} + + @ExpectedModifiers(Modifier.FINAL | Modifier.ABSTRACT | Modifier.PRIVATE) + private interface PrivateInterface {} + + /* + * Relevant excerpt of the Class.getModifiers() specification: + * + * If this {@code Class} object represents a primitive type or + * void, its {@code public}, {@code abstract}, and {@code final} + * modifiers are always {@code true}. + */ + private static void testPrimitives() { + Class[] testCases = { + void.class, + boolean.class, + byte.class, + short.class, + char.class, + int.class, + float.class, + long.class, + double.class, + }; + + for(var testCase : testCases) { + int actualModifiers = testCase.getModifiers(); + if ((Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL) != + actualModifiers) { + throw new RuntimeException("Bad modifiers " + + Modifier.toString(actualModifiers) + + " on primitive type " + testCase); + } + } + } +} + +@Retention(RetentionPolicy.RUNTIME) +@interface ExpectedModifiers { + int value() default 0; +} diff --git a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java index 647029a011f..10ebfd6aff2 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8266670 8291734 + * @bug 8266670 8291734 8296743 * @summary Test expected AccessFlag's on classes. */ @@ -98,23 +98,14 @@ public final class ClassAccessFlagTest { void.class // same access flag rules }; - var mustBePresent = Set.of(AccessFlag.PUBLIC, AccessFlag.FINAL); - var mustBeAbsent = Set.of(AccessFlag.PRIVATE, - AccessFlag.PROTECTED, - AccessFlag.INTERFACE); + var expected = Set.of(AccessFlag.PUBLIC, + AccessFlag.FINAL, + AccessFlag.ABSTRACT); for(var primClass : primitives) { - // PUBLIC must be present, PROTECTED and PRIVATE must be - // absent. - // FINAL must be present, INTERFACE must be absent. var accessFlags = primClass.accessFlags(); - if (!accessFlags.containsAll(mustBePresent)) { - throw new RuntimeException("Missing mandatory flags on " + - primClass); - } - - if (containsAny(accessFlags, mustBeAbsent)) { - throw new RuntimeException("Unexpected flags present on " + + if (!accessFlags.equals(expected)) { + throw new RuntimeException("Unexpected flags on " + primClass); } }