mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8296743: Tighten Class.getModifiers spec for array classes
Reviewed-by: rriggs, mchung, heidinga
This commit is contained in:
parent
3a15e8483c
commit
6fd1442123
3 changed files with 171 additions and 37 deletions
|
@ -1296,17 +1296,21 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* {@code abstract} and {@code interface}; they should be decoded
|
* {@code abstract} and {@code interface}; they should be decoded
|
||||||
* using the methods of class {@code Modifier}.
|
* using the methods of class {@code Modifier}.
|
||||||
*
|
*
|
||||||
* <p> If the underlying class is an array class, then its
|
* <p> If the underlying class is an array class:
|
||||||
* {@code public}, {@code private} and {@code protected}
|
* <ul>
|
||||||
* modifiers are the same as those of its component type. If this
|
* <li> its {@code public}, {@code private} and {@code protected}
|
||||||
* {@code Class} object represents a primitive type or void, its
|
* modifiers are the same as those of its component type
|
||||||
* {@code public} modifier is always {@code true}, and its
|
* <li> its {@code abstract} and {@code final} modifiers are always
|
||||||
* {@code protected} and {@code private} modifiers are always
|
* {@code true}
|
||||||
* {@code false}. If this {@code Class} object represents an array class, a
|
* <li> its interface modifier is always {@code false}, even when
|
||||||
* primitive type or void, then its {@code final} modifier is always
|
* the component type is an interface
|
||||||
* {@code true} and its interface modifier is always
|
* </ul>
|
||||||
* {@code false}. The values of its other modifiers are not determined
|
* If this {@code Class} object represents a primitive type or
|
||||||
* by this specification.
|
* 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}
|
* <p> The modifier encodings are defined in section {@jvms 4.1}
|
||||||
* of <cite>The Java Virtual Machine Specification</cite>.
|
* of <cite>The Java Virtual Machine Specification</cite>.
|
||||||
|
@ -1320,6 +1324,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* @since 1.1
|
* @since 1.1
|
||||||
* @jls 8.1.1 Class Modifiers
|
* @jls 8.1.1 Class Modifiers
|
||||||
* @jls 9.1.1. Interface Modifiers
|
* @jls 9.1.1. Interface Modifiers
|
||||||
|
* @jvms 4.1 The {@code ClassFile} Structure
|
||||||
*/
|
*/
|
||||||
@IntrinsicCandidate
|
@IntrinsicCandidate
|
||||||
public native int getModifiers();
|
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
|
* {@return an unmodifiable set of the {@linkplain AccessFlag access
|
||||||
* flags} for this class, possibly empty}
|
* flags} for this class, possibly empty}
|
||||||
*
|
*
|
||||||
* <p> If the underlying class is an array class, then its
|
* <p> If the underlying class is an array class:
|
||||||
* {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED}
|
* <ul>
|
||||||
* access flags are the same as those of its component type. If this
|
* <li> its {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED}
|
||||||
* {@code Class} object represents a primitive type or void, the
|
* access flags are the same as those of its component type
|
||||||
* {@code PUBLIC} access flag is present, and the
|
* <li> its {@code ABSTRACT} and {@code FINAL} flags are present
|
||||||
* {@code PROTECTED} and {@code PRIVATE} access flags are always
|
* <li> its {@code INTERFACE} flag is absent, even when the
|
||||||
* absent. If this {@code Class} object represents an array class, a
|
* component type is an interface
|
||||||
* primitive type or void, then the {@code FINAL} access flag is always
|
* </ul>
|
||||||
* present and the interface access flag is always
|
* If this {@code Class} object represents a primitive type or
|
||||||
* absent. The values of its other access flags are not determined
|
* void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and
|
||||||
* by this specification.
|
* {@code FINAL}.
|
||||||
|
* For {@code Class} objects representing void, primitive types, and
|
||||||
|
* arrays, access flags are absent other than as specified above.
|
||||||
*
|
*
|
||||||
* @see #getModifiers()
|
* @see #getModifiers()
|
||||||
* @jvms 4.1 The ClassFile Structure
|
* @jvms 4.1 The ClassFile Structure
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8266670 8291734
|
* @bug 8266670 8291734 8296743
|
||||||
* @summary Test expected AccessFlag's on classes.
|
* @summary Test expected AccessFlag's on classes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -98,23 +98,14 @@ public final class ClassAccessFlagTest {
|
||||||
void.class // same access flag rules
|
void.class // same access flag rules
|
||||||
};
|
};
|
||||||
|
|
||||||
var mustBePresent = Set.of(AccessFlag.PUBLIC, AccessFlag.FINAL);
|
var expected = Set.of(AccessFlag.PUBLIC,
|
||||||
var mustBeAbsent = Set.of(AccessFlag.PRIVATE,
|
AccessFlag.FINAL,
|
||||||
AccessFlag.PROTECTED,
|
AccessFlag.ABSTRACT);
|
||||||
AccessFlag.INTERFACE);
|
|
||||||
|
|
||||||
for(var primClass : primitives) {
|
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();
|
var accessFlags = primClass.accessFlags();
|
||||||
if (!accessFlags.containsAll(mustBePresent)) {
|
if (!accessFlags.equals(expected)) {
|
||||||
throw new RuntimeException("Missing mandatory flags on " +
|
throw new RuntimeException("Unexpected flags on " +
|
||||||
primClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containsAny(accessFlags, mustBeAbsent)) {
|
|
||||||
throw new RuntimeException("Unexpected flags present on " +
|
|
||||||
primClass);
|
primClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue