CSR :
|
|
Relates :
|
|
Relates :
|
Summary ------- Add enums and supporting methods to model JVM access flags as distinct from Java language modifiers. Problem ------- Language-level modifiers, such as `java.lang.reflect.Modifier`, do not always easily make to JVM-level access flags. Just given an integer bit mask, the semantic modifiers indicated in the mask cannot be interpreted without the context of what kind of element is being modified since the flags are not distinct. Example, the same bit is used to indicate volatile fields as bridge methods. Solution -------- Add a new `enum` class, `java.lang.reflect.AccessFlag` which explicitly models JVM access flags along with "getter" methods to map modifiers to a set of access flags for `java.lang.Class`, `java.lang.reflect.Field`, and elsewhere in core reflection. Specification ------------- --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -36,6 +36,7 @@ import java.io.ObjectStreamField; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AccessFlag; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -1312,6 +1313,7 @@ * * @return the {@code int} representing the modifiers for this class * @see java.lang.reflect.Modifier + * @see #accessFlags() * @see <a * href="{@docRoot}/java.base/java/lang/reflect/package-summary.html#LanguageJvmModel">Java * programming language and JVM modeling in core reflection</a> @@ -1322,6 +1324,25 @@ @IntrinsicCandidate public native int getModifiers(); + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag access + * flags} for this class, possibly empty} + * @see #getModifiers() + * @jvms 4.1 The ClassFile Structure + * @jvms 4.7.6 The InnerClasses Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + // This likely needs some refinement. Exploration of hidden + // classes, array classes. Location.CLASS allows SUPER and + // AccessFlag.MODULE which INNER_CLASS forbids. INNER_CLASS + // allows PRIVATE, PROTECTED, and STATIC, which are not + // allowed on Location.CLASS. + return AccessFlag.maskToAccessFlags(getModifiers(), + (isMemberClass() || isLocalClass() || isAnonymousClass()) ? + AccessFlag.Location.INNER_CLASS : + AccessFlag.Location.CLASS); + } /** * Gets the signers of this class. diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java index 832d24c07da0..66d84ceb96e7 100644 --- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java +++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java + /** + * {@return an unmodifiable set of the module {@linkplain AccessFlag + * requires flags, possibly empty}} + * @see #modifiers() + * @jvms 4.7.25 The Module Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + int mask = 0; + for (var modifier : mods) { + mask |= modifier.mask(); + } + return AccessFlag.maskToAccessFlags(mask, AccessFlag.Location.MODULE_REQUIRES); + } + /** * Return the module name. * @@ -417,6 +446,21 @@ public Set<Modifier> modifiers() { return mods; } + /** + * {@return an unmodifiable set of the module {@linkplain AccessFlag + * export flags} for this module descriptor, possibly empty} + * @see #modifiers() + * @jvms 4.7.25 The Module Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + int mask = 0; + for (var modifier : mods) { + mask |= modifier.mask(); + } + return AccessFlag.maskToAccessFlags(mask, AccessFlag.Location.MODULE_EXPORTS); + } + /** * Returns {@code true} if this is a qualified export. * @@ -620,6 +668,21 @@ public Set<Modifier> modifiers() { return mods; } + /** + * {@return an unmodifiable set of the module {@linkplain AccessFlag + * opens flags}, possibly empty} + * @see #modifiers() + * @jvms 4.7.25 The Module Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + int mask = 0; + for (var modifier : mods) { + mask |= modifier.mask(); + } + return AccessFlag.maskToAccessFlags(mask, AccessFlag.Location.MODULE_OPENS); + } + /** * Returns {@code true} if this is a qualified {@code Opens}. * @@ -1290,6 +1353,21 @@ public Set<Modifier> modifiers() { return modifiers; } + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag + * module flags}, possibly empty} + * @see #modifiers() + * @jvms 4.7.25 The Module Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + int mask = 0; + for (var modifier : modifiers) { + mask |= modifier.mask(); + } + return AccessFlag.maskToAccessFlags(mask, AccessFlag.Location.MODULE); + } + /** * <p> Returns {@code true} if this is an open module. </p> * diff --git a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java new file mode 100644 index 000000000000..e4b31130a0bc --- /dev/null +++ b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package java.lang.reflect; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import static java.util.Map.entry; + +/** + * Represents a JVM access or module-related flag on a runtime member, + * such as a {@linkplain Class class}, {@linkplain Field field}, or + * {@linkplain Executable method}. + * + * <P>JVM access and module-related flags are related to, but distinct + * from Java language {@linkplain Modifier modifiers}. Some modifiers + * and access flags have a one-to-one correspondence, such as {@code + * public}. In other cases, some language-level modifiers do + * <em>not</em> have an access flag, such as {@code sealed} (JVMS + * {@jvms 4.7.31}) and some access flags have no corresponding + * modifier, such as {@linkplain #SYNTHETIC synthetic}. + * + * <p>The values for the constants representing the access and module + * flags are taken from sections of <cite>The Java Virtual Machine + * Specification</cite> including {@jvms 4.1} (class access and + * property modifiers), {@jvms 4.5} (field access and property flags), + * {@jvms 4.6} (method access and property flags), {@jvms 4.7.6} + * (nested class access and property flags), {@jvms 4.7.24} (method + * parameters), and {@jvms 4.7.25} (module flags and requires, + * exports, and opens flags). + * + * <p>The {@linkplain #mask() mask} values for the different access + * flags are <em>not</em> distinct. Flags are defined for different + * kinds of JVM structures and the same bit position has different + * meanings in different contexts. For example, {@code 0x0000_0040} + * indicates a {@link #VOLATILE volatile} field but a {@linkplain + * #BRIDGE bridge method}; {@code 0x0000_0080} indicates a {@link + * #TRANSIENT transient} field but a {@linkplain #VARARGS variable + * arity (varargs)} method. + * + * @implSpec + * The access flag constants are ordered by non-decreasing mask + * value; that is the mask value of a constant is greater than or + * equal to the mask value of an immediate neighbor to its (syntactic) + * left. If new constants are added, this property will be + * maintained. That implies new constants will not necessarily be + * added at the end of the existing list. + * + * @see java.lang.reflect.Modifier + * @see java.lang.module.ModuleDescriptor.Modifier + * @see java.lang.module.ModuleDescriptor.Requires.Modifier + * @see java.lang.module.ModuleDescriptor.Exports.Modifier + * @see java.lang.module.ModuleDescriptor.Opens.Modifier + * @see java.compiler/javax.lang.model.element.Modifier + * @since 20 + */ +@SuppressWarnings("doclint:reference") // cross-module link +public enum AccessFlag { + /** + * The access flag {@code ACC_PUBLIC}, corresponding to the source + * modifier {@link Modifier#PUBLIC public} with a mask value of + * <code>{@value "0x%04x" Modifier#PUBLIC}</code>. + */ + PUBLIC(Modifier.PUBLIC, true, + Set.of(Location.CLASS, Location.FIELD, Location.METHOD, + Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_PRIVATE}, corresponding to the + * source modifier {@link Modifier#PRIVATE private} with a mask + * value of <code>{@value "0x%04x" Modifier#PRIVATE}</code>. + */ + PRIVATE(Modifier.PRIVATE, true, + Set.of(Location.FIELD, Location.METHOD, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_PROTECTED}, corresponding to the + * source modifier {@link Modifier#PROTECTED protected} with a mask + * value of <code>{@value "0x%04x" Modifier#PROTECTED}</code>. + */ + PROTECTED(Modifier.PROTECTED, true, + Set.of(Location.FIELD, Location.METHOD, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_STATIC}, corresponding to the source + * modifier {@link Modifier#STATIC static} with a mask value of + * <code>{@value "0x%04x" Modifier#STATIC}</code>. + */ + STATIC(Modifier.STATIC, true, + Set.of(Location.FIELD, Location.METHOD, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_FINAL}, corresponding to the source + * modifier {@link Modifier#FINAL final} with a mask + * value of <code>{@value "0x%04x" Modifier#FINAL}</code>. + */ + FINAL(Modifier.FINAL, true, + Set.of(Location.CLASS, Location.FIELD, Location.METHOD, + Location.INNER_CLASS, Location.METHOD_PARAMETER)), + + /** + * The access flag {@code ACC_SUPER} with a mask value of {@code + * 0x0020}. + */ + SUPER(0x0000_0020, false, Set.of(Location.CLASS)), + + /** + * The module flag {@code ACC_OPEN} with a mask value of {@code + * 0x0020}. + * @see java.lang.module.ModuleDescriptor#isOpen + */ + OPEN(0x0000_0020, false, Set.of(Location.MODULE)), + + /** + * The module requires flag {@code ACC_TRANSITIVE} with a mask + * value of {@code 0x0020}. + * @see java.lang.module.ModuleDescriptor.Requires.Modifier#TRANSITIVE + */ + TRANSITIVE(0x0000_0020, false, Set.of(Location.MODULE_REQUIRES)), + + /** + * The access flag {@code ACC_SYNCHRONIZED}, corresponding to the + * source modifier {@link Modifier#SYNCHRONIZED synchronized} with + * a mask value of <code>{@value "0x%04x" Modifier#SYNCHRONIZED}</code>. + */ + SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Set.of(Location.METHOD)), + + /** + * The module requires flag {@code ACC_STATIC_PHASE} with a mask + * value of {@code 0x0040}. + * @see java.lang.module.ModuleDescriptor.Requires.Modifier#STATIC + */ + STATIC_PHASE(0x0000_0040, false, Set.of(Location.MODULE_REQUIRES)), + + /** + * The access flag {@code ACC_VOLATILE}, corresponding to the + * source modifier {@link Modifier#VOLATILE volatile} with a mask + * value of <code>{@value "0x%04x" Modifier#VOLATILE}</code>. + */ + VOLATILE(Modifier.VOLATILE, true, Set.of(Location.FIELD)), + + /** + * The access flag {@code ACC_BRIDGE} with a mask value of + * <code>{@value "0x%04x" Modifier#BRIDGE}</code> + * @see Method#isBridge() + */ + BRIDGE(Modifier.BRIDGE, false, Set.of(Location.METHOD)), + + /** + * The access flag {@code ACC_TRANSIENT}, corresponding to the + * source modifier {@link Modifier#TRANSIENT transient} with a + * mask value of <code>{@value "0x%04x" Modifier#TRANSIENT}</code>. + */ + TRANSIENT(Modifier.TRANSIENT, true, Set.of(Location.FIELD)), + + /** + * The access flag {@code ACC_VARARGS} with a mask value of + <code>{@value "0x%04x" Modifier#VARARGS}</code>. + * @see Executable#isVarArgs() + */ + VARARGS(Modifier.VARARGS, false, Set.of(Location.METHOD)), + + /** + * The access flag {@code ACC_NATIVE}, corresponding to the source + * modifier {@link Modifier#NATIVE native} with a mask value of + * <code>{@value "0x%04x" Modifier#NATIVE}</code>. + */ + NATIVE(Modifier.NATIVE, true, Set.of(Location.METHOD)), + + /** + * The access flag {@code ACC_INTERFACE} with a mask value of + * {@code 0x0200}. + * @see Class#isInterface() + */ + INTERFACE(Modifier.INTERFACE, false, + Set.of(Location.CLASS, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_ABSTRACT}, corresponding to the + * source modifier {@link Modifier#ABSTRACT abstract} with a mask + * value of <code>{@value "0x%04x" Modifier#ABSTRACT}</code>. + */ + ABSTRACT(Modifier.ABSTRACT, true, + Set.of(Location.CLASS, Location.METHOD, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_STRICT}, corresponding to the source + * modifier {@link Modifier#STRICT strictfp} with a mask value of + * <code>{@value "0x%04x" Modifier#STRICT}</code>. + */ + STRICT(Modifier.STRICT, true, Set.of(Location.METHOD)), + + /** + * The access flag {@code ACC_SYNTHETIC} with a mask value of + * <code>{@value "0x%04x" Modifier#SYNTHETIC}</code>. + * @see Class#isSynthetic() + * @see Executable#isSynthetic() + * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC + */ + SYNTHETIC(Modifier.SYNTHETIC, false, + Set.of(Location.CLASS, Location.FIELD, Location.METHOD, + Location.INNER_CLASS, Location.METHOD_PARAMETER, + Location.MODULE, Location.MODULE_REQUIRES, + Location.MODULE_EXPORTS, Location.MODULE_OPENS)), + + /** + * The access flag {@code ACC_ANNOTATION} with a mask value of + * <code>{@value "0x%04x" Modifier#ANNOTATION}</code>. + * @see Class#isAnnotation() + */ + ANNOTATION(Modifier.ANNOTATION, false, + Set.of(Location.CLASS, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_ENUM} with a mask value of + * <code>{@value "0x%04x" Modifier#ENUM}</code>. + * @see Class#isEnum() + */ + ENUM(Modifier.ENUM, false, + Set.of(Location.CLASS, Location.FIELD, Location.INNER_CLASS)), + + /** + * The access flag {@code ACC_MANDATED} with a mask value of + * <code>{@value "0x%04x" Modifier#MANDATED}</code>. + */ + MANDATED(Modifier.MANDATED, false, + Set.of(Location.METHOD_PARAMETER, + Location.MODULE, Location.MODULE_REQUIRES, + Location.MODULE_EXPORTS, Location.MODULE_OPENS)), + + /** + * The access flag {@code ACC_MODULE} with a mask value of {@code + * 0x8000}. + */ + MODULE(0x0000_8000, false, Set.of(Location.CLASS)) + ; + + // May want to override toString for a different enum constant -> + // name mapping. + + private int mask; + private boolean sourceModifier; + + // Intentionally using Set rather than EnumSet since EnumSet is + // mutable. + private Set<Location> locations; + + private AccessFlag(int mask, boolean sourceModifier, Set<Location> locations) { + this.mask = mask; + this.sourceModifier = sourceModifier; + this.locations = locations; + } + + /** + * {@return the corresponding integer mask for the access flag} + */ + public int mask() { + return mask; + } + + /** + * {@return whether or not the flag has a directly corresponding + * modifier in the Java programming language} + */ + public boolean sourceModifier() { + return sourceModifier; + } + + /** + * {@return kinds of constructs the flag can be applied to} + */ + public Set<Location> locations() { + return locations; + } + + /** + * {@return an unmodifiable set of access flags for the given mask value + * appropriate for the location in question} + * + * @param mask bit mask of access flags + * @param location context to interpret mask value + * @throws IllegalArgumentException if the mask contains bit + * positions not support for the location in question + */ + public static Set<AccessFlag> maskToAccessFlags(int mask, Location location) { + Set<AccessFlag> result = java.util.EnumSet.noneOf(AccessFlag.class); + for (var accessFlag : LocationToFlags.locationToFlags.get(location)) { + int accessMask = accessFlag.mask(); + if ((mask & accessMask) != 0) { + result.add(accessFlag); + mask = mask & ~accessMask; + } + } + if (mask != 0) { + throw new IllegalArgumentException("Unmatched bit position 0x" + + Integer.toHexString(mask) + + " for location " + location); + } + return Collections.unmodifiableSet(result); + } + + /** + * A location within a class file where flags can be applied. + * + * Note that since these locations represent class file structures + * rather than language structures many language structures, such + * as constructors and interfaces, are <em>not</em> present. + * @since 20 + */ + public enum Location { + /** + * Class location. + * @jvms 4.1 The ClassFile Structure + */ + CLASS, + + /** + * Field location. + * @jvms 4.5 Fields + */ + FIELD, + + /** + * Method location. + * @jvms 4.6 Method + */ + METHOD, + + /** + * Inner class location. + * @jvms 4.7.6 The InnerClasses Attribute + */ + INNER_CLASS, + + /** + * Method parameter loccation. + * @jvms 4.7.24. The MethodParameters Attribute + */ + METHOD_PARAMETER, + + /** + * Module location + * @jvms 4.7.25. The Module Attribute + */ + MODULE, + + /** + * Module requires location + * @jvms 4.7.25. The Module Attribute + */ + MODULE_REQUIRES, + + /** + * Module exports location + * @jvms 4.7.25. The Module Attribute + */ + MODULE_EXPORTS, + + /** + * Module opens location + * @jvms 4.7.25. The Module Attribute + */ + MODULE_OPENS; + + } diff --git a/src/java.base/share/classes/java/lang/reflect/Executable.java b/src/java.base/share/classes/java/lang/reflect/Executable.java index d86a454c7511..e2e400aec2a3 100644 --- a/src/java.base/share/classes/java/lang/reflect/Executable.java +++ b/src/java.base/share/classes/java/lang/reflect/Executable.java @@ -28,6 +28,7 @@ import java.lang.annotation.*; import java.util.Arrays; import java.util.Map; +import java.util.Set; import java.util.Objects; import java.util.StringJoiner; import java.util.stream.Stream; @@ -204,9 +205,29 @@ String sharedToGenericString(int modifierMask, boolean isDefault) { /** * {@return the Java language {@linkplain Modifier modifiers} for * the executable represented by this object} + * @see #accessFlags */ public abstract int getModifiers(); + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag + * access flags} for the executable represented by this object, + * possibly empty} + * @see #getModifiers() + * @jvms 4.6 Methods + * @since 20 + */ + @Override + public Set<AccessFlag> accessFlags() { + return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.METHOD); + } + /** * Returns an array of {@code TypeVariable} objects that represent the * type variables declared by the generic declaration represented by this diff --git a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java index eb4677f01efb..602f72e4a35e 100644 --- a/src/java.base/share/classes/java/lang/reflect/Field.java +++ b/src/java.base/share/classes/java/lang/reflect/Field.java @@ -37,6 +37,7 @@ import sun.reflect.generics.scope.ClassScope; import java.lang.annotation.Annotation; import java.util.Map; +import java.util.Set; import java.util.Objects; import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.AnnotationSupport; @@ -202,6 +203,7 @@ public String getName() { * be used to decode the modifiers. * * @see Modifier + * @see #accessFlags() * @jls 8.3 Field Declarations * @jls 9.3 Field (Constant) Declarations */ @@ -209,6 +211,18 @@ public int getModifiers() { return modifiers; } + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag + * access flags} for this field, possibly empty} + * @see #getModifiers() + * @jvms 4.5 Fields + * @since 20 + */ + @Override + public Set<AccessFlag> accessFlags() { + return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.FIELD); + } + /** * Returns {@code true} if this field represents an element of * an enumerated class; returns {@code false} otherwise. diff --git a/src/java.base/share/classes/java/lang/reflect/Member.java b/src/java.base/share/classes/java/lang/reflect/Member.java index 1e8edc803b7c..7da4b2e34456 100644 --- a/src/java.base/share/classes/java/lang/reflect/Member.java +++ b/src/java.base/share/classes/java/lang/reflect/Member.java @@ -25,6 +25,8 @@ package java.lang.reflect; +import java.util.Set; + /** * Member is an interface that reflects identifying information about * a single member (a field or a method) or a constructor. @@ -76,9 +78,25 @@ public interface Member { * * @return the Java language modifiers for the underlying member * @see Modifier + * @see #accessFlags() */ public int getModifiers(); + + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag + * access flags} for this member, possibly empty} + * + * @implSpec + * The default implementation throws {@link + * UnsupportedOperationException}. + * @see #getModifiers() + * @since 20 + */ + public default Set<AccessFlag> accessFlags() { + throw new UnsupportedOperationException(); + } + /** * Returns {@code true} if this member was introduced by * the compiler; returns {@code false} otherwise. diff --git a/src/java.base/share/classes/java/lang/reflect/Parameter.java b/src/java.base/share/classes/java/lang/reflect/Parameter.java index 780319585ab2..8035bff7bc23 100644 --- a/src/java.base/share/classes/java/lang/reflect/Parameter.java +++ b/src/java.base/share/classes/java/lang/reflect/Parameter.java @@ -27,6 +27,7 @@ import java.lang.annotation.*; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.Objects; import sun.reflect.annotation.AnnotationSupport; @@ -161,6 +162,20 @@ public int getModifiers() { return modifiers; } + /** + * {@return an unmodifiable set of the {@linkplain AccessFlag + * access flags} for the parameter represented by this object, + * possibly empty} + * + * @see #getModifiers() + * @jvms 4.7.24 The MethodParameters Attribute + * @since 20 + */ + public Set<AccessFlag> accessFlags() { + return AccessFlag.maskToAccessFlags(getModifiers(), + AccessFlag.Location.METHOD_PARAMETER); + } + /** * Returns the name of the parameter. If the parameter's name is
|