8296743: Tighten Class.getModifiers spec for array classes

Reviewed-by: rriggs, mchung, heidinga
This commit is contained in:
Joe Darcy 2022-11-18 21:51:55 +00:00
parent 3a15e8483c
commit 6fd1442123
3 changed files with 171 additions and 37 deletions

View File

@ -1296,17 +1296,21 @@ public final class Class<T> implements java.io.Serializable,
* {@code abstract} and {@code interface}; they should be decoded
* using the methods of class {@code Modifier}.
*
* <p> 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.
* <p> If the underlying class is an array class:
* <ul>
* <li> its {@code public}, {@code private} and {@code protected}
* modifiers are the same as those of its component type
* <li> its {@code abstract} and {@code final} modifiers are always
* {@code true}
* <li> its interface modifier is always {@code false}, even when
* the component type is an interface
* </ul>
* 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.
*
* <p> The modifier encodings are defined in section {@jvms 4.1}
* of <cite>The Java Virtual Machine Specification</cite>.
@ -1320,6 +1324,7 @@ public final class Class<T> 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<T> implements java.io.Serializable,
* {@return an unmodifiable set of the {@linkplain AccessFlag access
* flags} for this class, possibly empty}
*
* <p> 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.
* <p> If the underlying class is an array class:
* <ul>
* <li> its {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED}
* access flags are the same as those of its component type
* <li> its {@code ABSTRACT} and {@code FINAL} flags are present
* <li> its {@code INTERFACE} flag is absent, even when the
* component type is an interface
* </ul>
* 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

View File

@ -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:
* <p> If the underlying class is an array class:
* <ul>
* <li> its {@code public}, {@code private} and {@code protected}
* modifiers are the same as those of its component type
* <li> its {@code final} and {@code abstract} modifiers are always
* {@code true}
* <li> its interface modifier is always {@code false}, even when
* the component type is an interface
* </ul>
*/
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;
}

View File

@ -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);
}
}