mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8289106: Add model of class file versions to core reflection
Reviewed-by: rriggs
This commit is contained in:
parent
c3d3662e52
commit
e44e3f0c19
6 changed files with 803 additions and 44 deletions
|
@ -28,6 +28,7 @@ package java.lang.reflect;
|
|||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import static java.util.Map.entry;
|
||||
|
||||
/**
|
||||
|
@ -70,7 +71,7 @@ import static java.util.Map.entry;
|
|||
* added at the end of the existing list.
|
||||
*
|
||||
* @apiNote
|
||||
* The JVM class file format has a new version defined for each new
|
||||
* 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
|
||||
|
@ -97,29 +98,44 @@ public enum AccessFlag {
|
|||
* <code>{@value "0x%04x" Modifier#PUBLIC}</code>.
|
||||
*/
|
||||
PUBLIC(Modifier.PUBLIC, true,
|
||||
Set.of(Location.CLASS, Location.FIELD, Location.METHOD,
|
||||
Location.INNER_CLASS)),
|
||||
Location.SET_PUBLIC_1,
|
||||
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 <code>{@value "0x%04x" Modifier#PRIVATE}</code>.
|
||||
*/
|
||||
PRIVATE(Modifier.PRIVATE, true, Location.SET_FIELD_METHOD_INNER_CLASS),
|
||||
PRIVATE(Modifier.PRIVATE, true, Location.SET_FIELD_METHOD_INNER_CLASS,
|
||||
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 <code>{@value "0x%04x" Modifier#PROTECTED}</code>.
|
||||
*/
|
||||
PROTECTED(Modifier.PROTECTED, true, Location.SET_FIELD_METHOD_INNER_CLASS),
|
||||
PROTECTED(Modifier.PROTECTED, true, Location.SET_FIELD_METHOD_INNER_CLASS,
|
||||
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
|
||||
* <code>{@value "0x%04x" Modifier#STATIC}</code>.
|
||||
*/
|
||||
STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS),
|
||||
STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS,
|
||||
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
|
||||
|
@ -127,8 +143,16 @@ public enum AccessFlag {
|
|||
* 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)),
|
||||
Location.SET_FINAL_8,
|
||||
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
|
||||
|
@ -138,77 +162,96 @@ public enum AccessFlag {
|
|||
* 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),
|
||||
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, Set.of(Location.MODULE)),
|
||||
OPEN(0x0000_0020, false, Location.SET_MODULE,
|
||||
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),
|
||||
TRANSITIVE(0x0000_0020, false, Location.SET_MODULE_REQUIRES,
|
||||
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 <code>{@value "0x%04x" Modifier#SYNCHRONIZED}</code>.
|
||||
*/
|
||||
SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Location.SET_METHOD),
|
||||
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),
|
||||
STATIC_PHASE(0x0000_0040, false, Location.SET_MODULE_REQUIRES,
|
||||
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 <code>{@value "0x%04x" Modifier#VOLATILE}</code>.
|
||||
*/
|
||||
VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD),
|
||||
/**
|
||||
* 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, Location.SET_FIELD, null),
|
||||
|
||||
/**
|
||||
* 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, Location.SET_METHOD),
|
||||
BRIDGE(Modifier.BRIDGE, false, Location.SET_METHOD,
|
||||
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 <code>{@value "0x%04x" Modifier#TRANSIENT}</code>.
|
||||
*/
|
||||
TRANSIENT(Modifier.TRANSIENT, true, Location.SET_FIELD),
|
||||
TRANSIENT(Modifier.TRANSIENT, true, Location.SET_FIELD, null),
|
||||
|
||||
/**
|
||||
* 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, Location.SET_METHOD),
|
||||
VARARGS(Modifier.VARARGS, false, Location.SET_METHOD,
|
||||
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
|
||||
* <code>{@value "0x%04x" Modifier#NATIVE}</code>.
|
||||
*/
|
||||
NATIVE(Modifier.NATIVE, true, Location.SET_METHOD),
|
||||
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),
|
||||
INTERFACE(Modifier.INTERFACE, false, Location.SET_CLASS_INNER_CLASS,
|
||||
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
|
||||
|
@ -216,7 +259,10 @@ public enum AccessFlag {
|
|||
* value of <code>{@value "0x%04x" Modifier#ABSTRACT}</code>.
|
||||
*/
|
||||
ABSTRACT(Modifier.ABSTRACT, true,
|
||||
Set.of(Location.CLASS, Location.METHOD, Location.INNER_CLASS)),
|
||||
Location.SET_CLASS_METHOD_INNER_CLASS,
|
||||
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
|
||||
|
@ -228,7 +274,12 @@ public enum AccessFlag {
|
|||
* major versions 46 through 60, inclusive (JVMS {@jvms 4.6}),
|
||||
* corresponding to Java SE 1.2 through 16.
|
||||
*/
|
||||
STRICT(Modifier.STRICT, true, Location.SET_METHOD),
|
||||
STRICT(Modifier.STRICT, true, Location.SET_METHOD,
|
||||
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
|
||||
|
@ -237,41 +288,63 @@ public enum AccessFlag {
|
|||
* @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)),
|
||||
SYNTHETIC(Modifier.SYNTHETIC, false, Location.SET_SYNTHETIC_9,
|
||||
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
|
||||
* <code>{@value "0x%04x" Modifier#ANNOTATION}</code>.
|
||||
* @see Class#isAnnotation()
|
||||
*/
|
||||
ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS),
|
||||
ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS,
|
||||
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
|
||||
* <code>{@value "0x%04x" Modifier#ENUM}</code>.
|
||||
* @see Class#isEnum()
|
||||
*/
|
||||
ENUM(Modifier.ENUM, false,
|
||||
Set.of(Location.CLASS, Location.FIELD, Location.INNER_CLASS)),
|
||||
ENUM(Modifier.ENUM, false, Location.SET_CLASS_FIELD_INNER_CLASS,
|
||||
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
|
||||
* <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)),
|
||||
MANDATED(Modifier.MANDATED, false, Location.SET_MANDATED_9,
|
||||
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)
|
||||
MODULE(0x0000_8000, false, Location.SET_CLASS,
|
||||
cffv -> {return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ?
|
||||
Location.SET_CLASS:
|
||||
Location.EMPTY_SET;})
|
||||
;
|
||||
|
||||
// May want to override toString for a different enum constant ->
|
||||
|
@ -283,11 +356,17 @@ public enum AccessFlag {
|
|||
// Intentionally using Set rather than EnumSet since EnumSet is
|
||||
// mutable.
|
||||
private final Set<Location> locations;
|
||||
// Lambda to implement locations(ClassFileFormatVersion cffv)
|
||||
private final Function<ClassFileFormatVersion, Set<Location>> cffvToLocations;
|
||||
|
||||
private AccessFlag(int mask, boolean sourceModifier, Set<Location> locations) {
|
||||
private AccessFlag(int mask,
|
||||
boolean sourceModifier,
|
||||
Set<Location> locations,
|
||||
Function<ClassFileFormatVersion, Set<Location>> cffvToLocations) {
|
||||
this.mask = mask;
|
||||
this.sourceModifier = sourceModifier;
|
||||
this.locations = locations;
|
||||
this.cffvToLocations = cffvToLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,12 +385,27 @@ public enum AccessFlag {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@return kinds of constructs the flag can be applied to}
|
||||
* {@return kinds of constructs the flag can be applied to in the
|
||||
* latest class file format version}
|
||||
*/
|
||||
public Set<Location> 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<Location> locations(ClassFileFormatVersion 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}
|
||||
|
@ -402,15 +496,55 @@ public enum AccessFlag {
|
|||
MODULE_OPENS;
|
||||
|
||||
// Repeated sets of locations used by AccessFlag constants
|
||||
private static final Set<Location> EMPTY_SET = Set.of();
|
||||
private static final Set<Location> SET_MODULE = Set.of(MODULE);
|
||||
private static final Set<Location> SET_CLASS_METHOD_INNER_CLASS =
|
||||
Set.of(CLASS, METHOD, INNER_CLASS);
|
||||
private static final Set<Location> SET_CLASS_FIELD_METHOD =
|
||||
Set.of(CLASS, FIELD, METHOD);
|
||||
private static final Set<Location> SET_CLASS_FIELD_INNER_CLASS =
|
||||
Set.of(CLASS, FIELD, INNER_CLASS);
|
||||
private static final Set<Location> SET_CLASS_FIELD_METHOD_INNER_CLASS =
|
||||
Set.of(CLASS, FIELD, METHOD, INNER_CLASS);
|
||||
private static final Set<Location> SET_CLASS_METHOD =
|
||||
Set.of(CLASS, METHOD);
|
||||
private static final Set<Location> SET_FIELD_METHOD =
|
||||
Set.of(FIELD, METHOD);
|
||||
private static final Set<Location> SET_FIELD_METHOD_INNER_CLASS =
|
||||
Set.of(FIELD, METHOD, INNER_CLASS);
|
||||
private static final Set<Location> SET_METHOD = Set.of(METHOD);
|
||||
private static final Set<Location> SET_METHOD_PARAM = Set.of(METHOD_PARAMETER);
|
||||
private static final Set<Location> SET_FIELD = Set.of(FIELD);
|
||||
private static final Set<Location> SET_CLASS = Set.of(CLASS);
|
||||
private static final Set<Location> SET_CLASS_INNER_CLASS =
|
||||
Set.of(CLASS, INNER_CLASS);
|
||||
private static final Set<Location> SET_MODULE_REQUIRES =
|
||||
Set.of(MODULE_REQUIRES);
|
||||
private static final Set<Location> SET_PUBLIC_1 =
|
||||
Set.of(CLASS, FIELD, METHOD, INNER_CLASS);
|
||||
private static final Set<Location> SET_FINAL_8 =
|
||||
Set.of(CLASS, FIELD, METHOD,
|
||||
INNER_CLASS, /* added in 1.1 */
|
||||
METHOD_PARAMETER); /* added in 8 */
|
||||
private static final Set<Location> SET_SYNTHETIC_7 =
|
||||
Set.of(CLASS, FIELD, METHOD,
|
||||
INNER_CLASS);
|
||||
private static final Set<Location> SET_SYNTHETIC_8 =
|
||||
Set.of(CLASS, FIELD, METHOD,
|
||||
INNER_CLASS, METHOD_PARAMETER);
|
||||
private static final Set<Location> 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<Location> SET_MANDATED_9 =
|
||||
Set.of(METHOD_PARAMETER, // From 8
|
||||
// Starting in 9
|
||||
MODULE, MODULE_REQUIRES,
|
||||
MODULE_EXPORTS, MODULE_OPENS);
|
||||
}
|
||||
|
||||
private static class LocationToFlags {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue