/* * Copyright (c) 2021, 2025, 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.lang.classfile.ClassModel; import java.lang.classfile.FieldModel; import java.lang.classfile.MethodModel; import java.lang.classfile.attribute.InnerClassInfo; import java.lang.classfile.attribute.MethodParameterInfo; import java.lang.classfile.attribute.ModuleAttribute; import java.lang.classfile.attribute.ModuleExportInfo; import java.lang.classfile.attribute.ModuleOpenInfo; import java.lang.classfile.attribute.ModuleRequireInfo; import java.lang.module.ModuleDescriptor; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import jdk.internal.vm.annotation.Stable; import static java.lang.classfile.ClassFile.*; import static java.lang.reflect.ClassFileFormatVersion.*; /** * 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}. * *

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 * not 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}. * *

The values for the constants representing the access and module * flags are taken from sections of The Java Virtual Machine * Specification 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). * *

The {@linkplain #mask() mask} values for the different access * flags are not 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. * * @apiNote * The JVM class file format has a {@linkplain ClassFileFormatVersion new version} defined for each new * {@linkplain Runtime.Version#feature() feature release}. A new class * file version may define new access flags or retire old ones. {@code * AccessFlag} is intended to model the set of access flags across * class file format versions. The range of versions an access flag is * recognized is not explicitly indicated in this API. See the current * The Java Virtual Machine Specification for * details. Unless otherwise indicated, access flags can be assumed to * be recognized in the {@linkplain Runtime#version() current * version}. * * @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 * {@value "0x%04x" Modifier#PUBLIC}. */ PUBLIC(Modifier.PUBLIC, true, Location.SET_CLASS_FIELD_METHOD_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))), /** * The access flag {@code ACC_PRIVATE}, corresponding to the * source modifier {@link Modifier#PRIVATE private}, with a mask * value of {@value "0x%04x" Modifier#PRIVATE}. */ PRIVATE(Modifier.PRIVATE, true, Location.SET_FIELD_METHOD_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_PROTECTED}, corresponding to the * source modifier {@link Modifier#PROTECTED protected}, with a mask * value of {@value "0x%04x" Modifier#PROTECTED}. */ PROTECTED(Modifier.PROTECTED, true, Location.SET_FIELD_METHOD_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_STATIC}, corresponding to the source * modifier {@link Modifier#STATIC static}, with a mask value of * {@value "0x%04x" Modifier#STATIC}. */ STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_FINAL}, corresponding to the source * modifier {@link Modifier#FINAL final}, with a mask * value of {@value "0x%04x" Modifier#FINAL}. */ FINAL(Modifier.FINAL, true, Location.SET_FINAL_8, List.of(Map.entry(RELEASE_7, Location.SET_CLASS_FIELD_METHOD_INNER_CLASS), Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))), /** * The access flag {@code ACC_SUPER} with a mask value of {@code * 0x0020}. * * @apiNote * In Java SE 8 and above, the JVM treats the {@code ACC_SUPER} * flag as set in every class file (JVMS {@jvms 4.1}). */ SUPER(0x0000_0020, false, Location.SET_CLASS, List.of()), /** * The module flag {@code ACC_OPEN} with a mask value of {@code * 0x0020}. * @see java.lang.module.ModuleDescriptor#isOpen */ OPEN(0x0000_0020, false, Location.SET_MODULE, List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), /** * 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, Location.SET_MODULE_REQUIRES, List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), /** * The access flag {@code ACC_SYNCHRONIZED}, corresponding to the * source modifier {@link Modifier#SYNCHRONIZED synchronized}, with * a mask value of {@value "0x%04x" Modifier#SYNCHRONIZED}. */ SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Location.SET_METHOD, List.of()), /** * 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, Location.SET_MODULE_REQUIRES, List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), /** * The access flag {@code ACC_VOLATILE}, corresponding to the * source modifier {@link Modifier#VOLATILE volatile}, with a mask * value of {@value "0x%04x" Modifier#VOLATILE}. */ VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD, List.of()), /** * The access flag {@code ACC_BRIDGE} with a mask value of * {@value "0x%04x" Modifier#BRIDGE} * @see Method#isBridge() */ BRIDGE(Modifier.BRIDGE, false, Location.SET_METHOD, List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_TRANSIENT}, corresponding to the * source modifier {@link Modifier#TRANSIENT transient}, with a * mask value of {@value "0x%04x" Modifier#TRANSIENT}. */ TRANSIENT(Modifier.TRANSIENT, true, Location.SET_FIELD, List.of()), /** * The access flag {@code ACC_VARARGS} with a mask value of * {@value "0x%04x" Modifier#VARARGS}. * @see Executable#isVarArgs() */ VARARGS(Modifier.VARARGS, false, Location.SET_METHOD, List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_NATIVE}, corresponding to the source * modifier {@link Modifier#NATIVE native}, with a mask value of * {@value "0x%04x" Modifier#NATIVE}. */ NATIVE(Modifier.NATIVE, true, Location.SET_METHOD, List.of()), /** * The access flag {@code ACC_INTERFACE} with a mask value of * {@code 0x0200}. * @see Class#isInterface() */ INTERFACE(Modifier.INTERFACE, false, Location.SET_CLASS_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_CLASS))), /** * The access flag {@code ACC_ABSTRACT}, corresponding to the * source modifier {@link Modifier#ABSTRACT abstract}, with a mask * value of {@value "0x%04x" Modifier#ABSTRACT}. */ ABSTRACT(Modifier.ABSTRACT, true, Location.SET_CLASS_METHOD_INNER_CLASS, List.of(Map.entry(RELEASE_0, Location.SET_CLASS_METHOD))), /** * The access flag {@code ACC_STRICT}, corresponding to the source * modifier {@link Modifier#STRICT strictfp}, with a mask value of * {@value "0x%04x" Modifier#STRICT}. * * @apiNote * The {@code ACC_STRICT} access flag is defined for class file * major versions 46 through 60, inclusive (JVMS {@jvms 4.6}), * corresponding to Java SE 1.2 through 16. */ STRICT(Modifier.STRICT, true, Location.EMPTY_SET, List.of(Map.entry(RELEASE_16, Location.SET_METHOD), Map.entry(RELEASE_1, Location.EMPTY_SET))), /** * The access flag {@code ACC_SYNTHETIC} with a mask value of * {@value "0x%04x" Modifier#SYNTHETIC}. * @see Class#isSynthetic() * @see Executable#isSynthetic() * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC */ SYNTHETIC(Modifier.SYNTHETIC, false, Location.SET_SYNTHETIC_9, List.of(Map.entry(RELEASE_8, Location.SET_SYNTHETIC_8), Map.entry(RELEASE_7, Location.SET_SYNTHETIC_5), Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_ANNOTATION} with a mask value of * {@value "0x%04x" Modifier#ANNOTATION}. * @see Class#isAnnotation() */ ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS, List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_ENUM} with a mask value of * {@value "0x%04x" Modifier#ENUM}. * @see Class#isEnum() */ ENUM(Modifier.ENUM, false, Location.SET_CLASS_FIELD_INNER_CLASS, List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_MANDATED} with a mask value of * {@value "0x%04x" Modifier#MANDATED}. */ MANDATED(Modifier.MANDATED, false, Location.SET_MANDATED_9, List.of(Map.entry(RELEASE_8, Location.SET_METHOD_PARAM), Map.entry(RELEASE_7, Location.EMPTY_SET))), /** * The access flag {@code ACC_MODULE} with a mask value of {@code * 0x8000}. */ MODULE(0x0000_8000, false, Location.SET_CLASS, List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), ; // May want to override toString for a different enum constant -> // name mapping. private final int mask; private final boolean sourceModifier; // immutable private final Set locations; // historical locations up to a given version; immutable private final List>> historicalLocations; private AccessFlag(int mask, boolean sourceModifier, Set locations, List>> historicalLocations) { this.mask = mask; this.sourceModifier = sourceModifier; this.locations = locations; this.historicalLocations = Location.ensureHistoryOrdered(historicalLocations); } /** * {@return the corresponding mask for the access flag} The mask has * exactly one bit set and is in the range of {@code char}. */ public int mask() { return mask; } /** * {@return whether or not this flag has a directly corresponding * modifier in the Java programming language} */ public boolean sourceModifier() { return sourceModifier; } /** * {@return locations this flag can be applied to in the current class file * format version} *

* This method returns an empty set if this flag is not defined in * the current class file format version. */ public Set locations() { return locations; } /** * {@return locations this flag can be applied to in the given class file * format version} *

* This method returns an empty set if this flag is not defined in * the given {@code cffv}. * * @param cffv the class file format version to use * @throws NullPointerException if the parameter is {@code null} */ public Set locations(ClassFileFormatVersion cffv) { return Location.findInHistory(locations, historicalLocations, cffv); } /** * {@return an unmodifiable set of access flags for the given mask value * appropriate for the location in the current class file format version} * * @param mask bit mask of access flags * @param location context to interpret mask value * @throws IllegalArgumentException if the mask contains bit * positions not defined for the location in the current class file format * @throws NullPointerException if {@code location} is {@code null} */ public static Set maskToAccessFlags(int mask, Location location) { var definition = findDefinition(location); // null checks location int unmatchedMask = mask & (~location.flagsMask()); if (unmatchedMask != 0) { throw new IllegalArgumentException("Unmatched bit position 0x" + Integer.toHexString(unmatchedMask) + " for location " + location); } return new AccessFlagSet(definition, mask); } /** * {@return an unmodifiable set of access flags for the given mask value * appropriate for the location in the given class file format version} * * @param mask bit mask of access flags * @param location context to interpret mask value * @param cffv the class file format to interpret mask value * @throws IllegalArgumentException if the mask contains bit * positions not defined for the location in the given class file format * @throws NullPointerException if {@code location} or {@code cffv} is {@code null} * @since 25 */ public static Set maskToAccessFlags(int mask, Location location, ClassFileFormatVersion cffv) { var definition = findDefinition(location); // null checks location int unmatchedMask = mask & (~location.flagsMask(cffv)); // null checks cffv if (unmatchedMask != 0) { throw new IllegalArgumentException("Unmatched bit position 0x" + Integer.toHexString(unmatchedMask) + " for location " + location + " for class file format " + cffv); } return new AccessFlagSet(definition, mask); } /** * A location within a {@code class} file where flags can be applied. *

* Note that since these locations represent {@code class} file structures * rather than language structures, many language structures, such * as constructors and interfaces, are not present. * @since 20 */ public enum Location { /** * Class location. * * @see Class#accessFlags() * @see ClassModel#flags() * @see Modifier#classModifiers() * @see Modifier#interfaceModifiers() * @jvms 4.1 The {@code ClassFile} Structure */ CLASS(ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM | ACC_MODULE, List.of(Map.entry(RELEASE_8, // no module ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM), Map.entry(RELEASE_4, // no synthetic, annotation, enum ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT))), /** * Field location. * * @see Field#accessFlags() * @see FieldModel#flags() * @see Modifier#fieldModifiers() * @jvms 4.5 Fields */ FIELD(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM, List.of(Map.entry(RELEASE_4, // no synthetic, enum ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT))), /** * Method location. * * @see Executable#accessFlags() * @see MethodModel#flags() * @see Modifier#methodModifiers() * @see Modifier#constructorModifiers() * @jvms 4.6 Methods */ METHOD(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_SYNTHETIC, List.of(Map.entry(RELEASE_16, // had strict ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC), Map.entry(RELEASE_4, // no bridge, varargs, synthetic ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT), Map.entry(RELEASE_1, // no strict ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_NATIVE | ACC_ABSTRACT))), /** * Inner class location. * * @see Class#accessFlags() * @see InnerClassInfo#flags() * @see Modifier#classModifiers() * @see Modifier#interfaceModifiers() * @jvms 4.7.6 The {@code InnerClasses} Attribute */ INNER_CLASS(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM, List.of(Map.entry(RELEASE_4, // no synthetic, annotation, enum ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT), Map.entry(RELEASE_0, 0))), // did not exist /** * Method parameter location. * * @see Parameter#accessFlags() * @see MethodParameterInfo#flags() * @see Modifier#parameterModifiers() * @jvms 4.7.24 The {@code MethodParameters} Attribute */ METHOD_PARAMETER(ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED, List.of(Map.entry(RELEASE_7, 0))), // did not exist /** * Module location. * * @see ModuleDescriptor#accessFlags() * @see ModuleAttribute#moduleFlags() * @jvms 4.7.25 The {@code Module} Attribute */ MODULE(ACC_OPEN | ACC_SYNTHETIC | ACC_MANDATED, List.of(Map.entry(RELEASE_8, 0))), // did not exist /** * Module requires location. * * @see ModuleDescriptor.Requires#accessFlags() * @see ModuleRequireInfo#requiresFlags() * @jvms 4.7.25 The {@code Module} Attribute */ MODULE_REQUIRES(ACC_TRANSITIVE | ACC_STATIC_PHASE | ACC_SYNTHETIC | ACC_MANDATED, List.of(Map.entry(RELEASE_8, 0))), // did not exist /** * Module exports location. * * @see ModuleDescriptor.Exports#accessFlags() * @see ModuleExportInfo#exportsFlags() * @jvms 4.7.25 The {@code Module} Attribute */ MODULE_EXPORTS(ACC_SYNTHETIC | ACC_MANDATED, List.of(Map.entry(RELEASE_8, 0))), // did not exist /** * Module opens location. * * @see ModuleDescriptor.Opens#accessFlags() * @see ModuleOpenInfo#opensFlags() * @jvms 4.7.25 The {@code Module} Attribute */ MODULE_OPENS(ACC_SYNTHETIC | ACC_MANDATED, List.of(Map.entry(RELEASE_8, 0))), // did not exist ; // Repeated sets of locations used by AccessFlag constants private static final Set EMPTY_SET = Set.of(); private static final Set SET_MODULE = Set.of(MODULE); private static final Set SET_CLASS_METHOD_INNER_CLASS = Set.of(CLASS, METHOD, INNER_CLASS); private static final Set SET_CLASS_FIELD_METHOD = Set.of(CLASS, FIELD, METHOD); private static final Set SET_CLASS_FIELD_INNER_CLASS = Set.of(CLASS, FIELD, INNER_CLASS); private static final Set SET_CLASS_FIELD_METHOD_INNER_CLASS = Set.of(CLASS, FIELD, METHOD, INNER_CLASS); private static final Set SET_CLASS_METHOD = Set.of(CLASS, METHOD); private static final Set SET_FIELD_METHOD = Set.of(FIELD, METHOD); private static final Set SET_FIELD_METHOD_INNER_CLASS = Set.of(FIELD, METHOD, INNER_CLASS); private static final Set SET_METHOD = Set.of(METHOD); private static final Set SET_METHOD_PARAM = Set.of(METHOD_PARAMETER); private static final Set SET_FIELD = Set.of(FIELD); private static final Set SET_CLASS = Set.of(CLASS); private static final Set SET_CLASS_INNER_CLASS = Set.of(CLASS, INNER_CLASS); private static final Set SET_MODULE_REQUIRES = Set.of(MODULE_REQUIRES); private static final Set SET_FINAL_8 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS, /* added in 1.1 */ METHOD_PARAMETER); /* added in 8 */ private static final Set SET_SYNTHETIC_5 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS); private static final Set SET_SYNTHETIC_8 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS, METHOD_PARAMETER); private static final Set SET_SYNTHETIC_9 = // Added as an access flag in 5.0 Set.of(CLASS, FIELD, METHOD, INNER_CLASS, METHOD_PARAMETER, // Added in 8 // Module-related items added in 9 MODULE, MODULE_REQUIRES, MODULE_EXPORTS, MODULE_OPENS); private static final Set SET_MANDATED_9 = Set.of(METHOD_PARAMETER, // From 8 // Starting in 9 MODULE, MODULE_REQUIRES, MODULE_EXPORTS, MODULE_OPENS); private final int flagsMask; private final List> historicalFlagsMasks; Location(int flagsMask, List> historicalFlagsMasks) { this.flagsMask = flagsMask; this.historicalFlagsMasks = ensureHistoryOrdered(historicalFlagsMasks); } // Ensures the historical versions are from newest to oldest and do not include the latest // These 2 utilities reside in Location because Location must be initialized before AccessFlag private static List> ensureHistoryOrdered( List> history) { ClassFileFormatVersion lastVersion = ClassFileFormatVersion.latest(); for (var e : history) { var historyVersion = e.getKey(); if (lastVersion.compareTo(historyVersion) <= 0) { throw new IllegalArgumentException("Versions out of order"); } lastVersion = historyVersion; } return history; } private static T findInHistory(T candidate, List> history, ClassFileFormatVersion cffv) { Objects.requireNonNull(cffv); for (var e : history) { if (e.getKey().compareTo(cffv) < 0) { // last version found was valid return candidate; } candidate = e.getValue(); } return candidate; } /** * {@return the union of masks of all access flags defined for * this location in the current class file format version} *

* This method returns {@code 0} if this location does not exist in * the current class file format version. * * @since 25 */ public int flagsMask() { return flagsMask; } /** * {@return the union of masks of all access flags defined for * this location in the given class file format version} *

* This method returns {@code 0} if this location does not exist in * the given {@code cffv}. * * @param cffv the class file format version * @throws NullPointerException if {@code cffv} is {@code null} * @since 25 */ public int flagsMask(ClassFileFormatVersion cffv) { return findInHistory(flagsMask, historicalFlagsMasks, cffv); } /** * {@return the set of access flags defined for this location in the * current class file format version} The set is immutable. *

* This method returns an empty set if this location does not exist * in the current class file format version. * * @since 25 */ public Set flags() { return new AccessFlagSet(findDefinition(this), flagsMask()); } /** * {@return the set of access flags defined for this location in the * given class file format version} The set is immutable. *

* This method returns an empty set if this location does not exist * in the given {@code cffv}. * * @param cffv the class file format version * @throws NullPointerException if {@code cffv} is {@code null} * @since 25 */ public Set flags(ClassFileFormatVersion cffv) { // implicit null check cffv return new AccessFlagSet(findDefinition(this), flagsMask(cffv)); } } private static AccessFlag[] createDefinition(AccessFlag... known) { var ret = new AccessFlag[Character.SIZE]; for (var flag : known) { var mask = flag.mask; int pos = Integer.numberOfTrailingZeros(mask); assert ret[pos] == null : ret[pos] + " " + flag; ret[pos] = flag; } return ret; } // Will take extra args in the future for valhalla switch private static AccessFlag[] findDefinition(Location location) { return switch (location) { case CLASS -> CLASS_FLAGS; case FIELD -> FIELD_FLAGS; case METHOD -> METHOD_FLAGS; case INNER_CLASS -> INNER_CLASS_FLAGS; case METHOD_PARAMETER -> METHOD_PARAMETER_FLAGS; case MODULE -> MODULE_FLAGS; case MODULE_REQUIRES -> MODULE_REQUIRES_FLAGS; case MODULE_EXPORTS -> MODULE_EXPORTS_FLAGS; case MODULE_OPENS -> MODULE_OPENS_FLAGS; }; } private static final @Stable AccessFlag[] // Can use stable array and lazy init in the future CLASS_FLAGS = createDefinition(PUBLIC, FINAL, SUPER, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM, MODULE), FIELD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, VOLATILE, TRANSIENT, SYNTHETIC, ENUM), METHOD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, BRIDGE, VARARGS, NATIVE, ABSTRACT, STRICT, SYNTHETIC), INNER_CLASS_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM), METHOD_PARAMETER_FLAGS = createDefinition(FINAL, SYNTHETIC, MANDATED), MODULE_FLAGS = createDefinition(OPEN, SYNTHETIC, MANDATED), MODULE_REQUIRES_FLAGS = createDefinition(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED), MODULE_EXPORTS_FLAGS = createDefinition(SYNTHETIC, MANDATED), MODULE_OPENS_FLAGS = createDefinition(SYNTHETIC, MANDATED); private static int undefinedMask(AccessFlag[] definition, int mask) { assert definition.length == Character.SIZE; int definedMask = 0; for (int i = 0; i < Character.SIZE; i++) { if (definition[i] != null) { definedMask |= 1 << i; } } return mask & ~definedMask; } private static final class AccessFlagSet extends AbstractSet { private final @Stable AccessFlag[] definition; private final int mask; // all mutating methods throw UnsupportedOperationException @Override public boolean add(AccessFlag e) { throw uoe(); } @Override public boolean addAll(Collection c) { throw uoe(); } @Override public void clear() { throw uoe(); } @Override public boolean remove(Object o) { throw uoe(); } @Override public boolean removeAll(Collection c) { throw uoe(); } @Override public boolean removeIf(Predicate filter) { throw uoe(); } @Override public boolean retainAll(Collection c) { throw uoe(); } private static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); } private AccessFlagSet(AccessFlag[] definition, int mask) { assert undefinedMask(definition, mask) == 0 : mask; this.definition = definition; this.mask = mask; } @Override public Iterator iterator() { return new AccessFlagIterator(definition, mask); } @Override public void forEach(Consumer action) { Objects.requireNonNull(action); // in case of empty for (int i = 0; i < Character.SIZE; i++) { if ((mask & (1 << i)) != 0) { action.accept(definition[i]); } } } private static final class AccessFlagIterator implements Iterator { private final @Stable AccessFlag[] definition; private int remainingMask; private AccessFlagIterator(AccessFlag[] definition, int remainingMask) { this.definition = definition; this.remainingMask = remainingMask; } @Override public boolean hasNext() { return remainingMask != 0; } @Override public AccessFlag next() { int flagBit = Integer.lowestOneBit(remainingMask); if (flagBit == 0) { throw new NoSuchElementException(); } remainingMask &= ~flagBit; return definition[Integer.numberOfTrailingZeros(flagBit)]; } } @Override public int size() { return Integer.bitCount(mask); } @Override public boolean contains(Object o) { if (Objects.requireNonNull(o) instanceof AccessFlag flag) { int bit = flag.mask; return (bit & mask) != 0 && definition[Integer.numberOfTrailingZeros(bit)] == flag; } return false; } @Override public boolean isEmpty() { return mask == 0; } } }