Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8296743: Tighten Class.getModifiers spec for array classes
Reviewed-by: rriggs, mchung, heidinga
  • Loading branch information
jddarcy committed Nov 18, 2022
1 parent 3a15e84 commit 6fd1442
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 37 deletions.
51 changes: 29 additions & 22 deletions src/java.base/share/classes/java/lang/Class.java
Expand Up @@ -1296,17 +1296,21 @@ private Class<?> elementType() {
* {@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>.
Expand All @@ -1320,6 +1324,7 @@ private Class<?> elementType() {
* @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();
Expand All @@ -1328,17 +1333,19 @@ private Class<?> elementType() {
* {@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
Expand Down
@@ -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;
}
21 changes: 6 additions & 15 deletions test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java
Expand Up @@ -23,7 +23,7 @@

/*
* @test
* @bug 8266670 8291734
* @bug 8266670 8291734 8296743
* @summary Test expected AccessFlag's on classes.
*/

Expand Down Expand Up @@ -98,23 +98,14 @@ private static void checkPrimitives() {
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);
}
}
Expand Down

1 comment on commit 6fd1442

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.