All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.dmlloyd.classfile.extras.reflect.AccessFlag Maven / Gradle / Ivy

There is a newer version: 24.cr2
Show newest version
/*
 * Copyright (c) 2021, 2024, 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 io.github.dmlloyd.classfile.extras.reflect;

import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Objects;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
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}.
 *
 * 

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 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 * @since 20 */ @SuppressWarnings("doclint:reference") // cross-module link public enum AccessFlag { // Note to maintainers: anonymous class instances are used rather // than lambdas to initialize the functions used for the // cffvToLocations field to avoid using lambdas too early in JDK // initialization. /** * 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_PUBLIC_1, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv == ClassFileFormatVersion.RELEASE_0) ? Location.SET_CLASS_FIELD_METHOD: Location.SET_PUBLIC_1; } }), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv == ClassFileFormatVersion.RELEASE_0) ? Location.SET_FIELD_METHOD: Location.SET_FIELD_METHOD_INNER_CLASS; } }), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv == ClassFileFormatVersion.RELEASE_0) ? Location.SET_FIELD_METHOD: Location.SET_FIELD_METHOD_INNER_CLASS; } }), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv == ClassFileFormatVersion.RELEASE_0) ? Location.SET_FIELD_METHOD: Location.SET_FIELD_METHOD_INNER_CLASS;} }), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { if (cffv.compareTo(ClassFileFormatVersion.RELEASE_8) >= 0) { return Location.SET_FINAL_8; } else { return (cffv == ClassFileFormatVersion.RELEASE_0) ? Location.SET_CLASS_FIELD_METHOD : Location.SET_CLASS_FIELD_METHOD_INNER_CLASS; } } }), /** * 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, null), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? Location.SET_MODULE: 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? Location.SET_MODULE_REQUIRES: 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, null), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? Location.SET_MODULE_REQUIRES: 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, null), /** * The access flag {@code ACC_BRIDGE} with a mask value of * {@value "0x%04x" Modifier#BRIDGE} * @see Method#isBridge() */ BRIDGE(1 << 6 /* BRIDGE == 64 */, false, Location.SET_METHOD, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? Location.SET_METHOD: 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, null), /** * The access flag {@code ACC_VARARGS} with a mask value of {@value "0x%04x" Modifier#VARARGS}. * @see Executable#isVarArgs() */ VARARGS(1 << 7 /* VARARGS == 128 */, false, Location.SET_METHOD, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? Location.SET_METHOD: 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, null), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_0) == 0 ) ? Location.SET_CLASS: Location.SET_CLASS_INNER_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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_0) == 0 ) ? Location.SET_CLASS_METHOD: Location.SET_CLASS_METHOD_INNER_CLASS;} }), /** * 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, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? Location.SET_METHOD: 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(1 << 12 /* SYNTHETIC == 4096 */, false, Location.SET_SYNTHETIC_9, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { if (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) return Location.SET_SYNTHETIC_9; else { return switch(cffv) { case RELEASE_7 -> Location.SET_SYNTHETIC_7; case RELEASE_8 -> Location.SET_SYNTHETIC_8; default -> Location.EMPTY_SET; }; } } }), /** * The access flag {@code ACC_ANNOTATION} with a mask value of * {@value "0x%04x" Modifier#ANNOTATION}. * @see Class#isAnnotation() */ ANNOTATION(1 << 13 /* ANNOTATION == 8192 */, false, Location.SET_CLASS_INNER_CLASS, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? Location.SET_CLASS_INNER_CLASS: Location.EMPTY_SET;} }), /** * The access flag {@code ACC_ENUM} with a mask value of * {@value "0x%04x" Modifier#ENUM}. * @see Class#isEnum() */ ENUM(1 << 14 /* ENUM == 16384 */, false, Location.SET_CLASS_FIELD_INNER_CLASS, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? Location.SET_CLASS_FIELD_INNER_CLASS: Location.EMPTY_SET;} }), /** * The access flag {@code ACC_MANDATED} with a mask value of * {@value "0x%04x" Modifier#MANDATED}. */ MANDATED(1 << 15 /* MANDATED == 32768 */, false, Location.SET_MANDATED_9, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { if (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) { return Location.SET_MANDATED_9; } else { return (cffv == ClassFileFormatVersion.RELEASE_8) ? Location.SET_METHOD_PARAM: Location.EMPTY_SET; } } }), /** * The access flag {@code ACC_MODULE} with a mask value of {@code * 0x8000}. */ MODULE(0x0000_8000, false, Location.SET_CLASS, new Function>() { @Override public Set apply(ClassFileFormatVersion cffv) { return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? Location.SET_CLASS: Location.EMPTY_SET;} }) ; // May want to override toString for a different enum constant -> // name mapping. private final int mask; private final boolean sourceModifier; // Intentionally using Set rather than EnumSet since EnumSet is // mutable. private final Set locations; // Lambda to implement locations(ClassFileFormatVersion cffv) private final Function> cffvToLocations; AccessFlag(int mask, boolean sourceModifier, Set locations, Function> cffvToLocations) { this.mask = mask; this.sourceModifier = sourceModifier; this.locations = locations; this.cffvToLocations = cffvToLocations; } /** * {@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 in the * latest class file format version} */ public Set locations() { return locations; } /** * {@return kinds of constructs the flag can be applied to in the * given class file format version} * @param cffv the class file format version to use * @throws NullPointerException if the parameter is {@code null} */ public Set locations(ClassFileFormatVersion cffv) { Objects.requireNonNull(cffv); if (cffvToLocations == null) { return locations; } else { return cffvToLocations.apply(cffv); } } /** * {@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 maskToAccessFlags(int mask, Location location) { Set 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 not 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 Methods */ METHOD, /** * Inner class location. * @jvms 4.7.6 The InnerClasses Attribute */ INNER_CLASS, /** * Method parameter location. * @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; // 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_PUBLIC_1 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS); 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_7 = 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 7 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 static class LocationToFlags { private static final Map> locationToFlags = Map.ofEntries(entry(Location.CLASS, Set.of(PUBLIC, FINAL, SUPER, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM, AccessFlag.MODULE)), entry(Location.FIELD, Set.of(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, VOLATILE, TRANSIENT, SYNTHETIC, ENUM)), entry(Location.METHOD, Set.of(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, BRIDGE, VARARGS, NATIVE, ABSTRACT, STRICT, SYNTHETIC)), entry(Location.INNER_CLASS, Set.of(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM)), entry(Location.METHOD_PARAMETER, Set.of(FINAL, SYNTHETIC, MANDATED)), entry(Location.MODULE, Set.of(OPEN, SYNTHETIC, MANDATED)), entry(Location.MODULE_REQUIRES, Set.of(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED)), entry(Location.MODULE_EXPORTS, Set.of(SYNTHETIC, MANDATED)), entry(Location.MODULE_OPENS, Set.of(SYNTHETIC, MANDATED))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy