mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8297271: AccessFlag.maskToAccessFlags should be specific to class file version
Reviewed-by: rriggs
This commit is contained in:
parent
34807df762
commit
bee273d6b4
@ -1384,10 +1384,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||
isAnonymousClass() || isArray()) ?
|
||||
AccessFlag.Location.INNER_CLASS :
|
||||
AccessFlag.Location.CLASS;
|
||||
return AccessFlag.maskToAccessFlags((location == AccessFlag.Location.CLASS) ?
|
||||
getClassAccessFlagsRaw() :
|
||||
getModifiers(),
|
||||
location);
|
||||
return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ?
|
||||
getClassAccessFlagsRaw() : getModifiers(), location, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4125,7 +4123,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* type is returned. If the class is a primitive type then the latest class
|
||||
* file major version is returned and zero is returned for the minor version.
|
||||
*/
|
||||
private int getClassFileVersion() {
|
||||
int getClassFileVersion() {
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
return c.getClassFileVersion0();
|
||||
}
|
||||
|
||||
@ -2017,6 +2017,9 @@ public final class System {
|
||||
E[] getEnumConstantsShared(Class<E> klass) {
|
||||
return klass.getEnumConstantsShared();
|
||||
}
|
||||
public int classFileVersion(Class<?> clazz) {
|
||||
return clazz.getClassFileVersion();
|
||||
}
|
||||
public void blockedOn(Interruptible b) {
|
||||
Thread.currentThread().blockedOn(b);
|
||||
}
|
||||
|
||||
@ -372,18 +372,17 @@ public enum AccessFlag {
|
||||
|
||||
/**
|
||||
* {@return an unmodifiable set of access flags for the given mask value
|
||||
* appropriate for the location in question}
|
||||
* 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 support for the location in question
|
||||
* positions not defined for the location in the current class file format
|
||||
* @throws NullPointerException if {@code location} is {@code null}
|
||||
*/
|
||||
public static Set<AccessFlag> maskToAccessFlags(int mask, Location location) {
|
||||
var definition = findDefinition(location);
|
||||
int flagsMask = location.flagsMask();
|
||||
int parsingMask = location == Location.METHOD ? flagsMask | ACC_STRICT : flagsMask; // flagMask lacks strictfp
|
||||
int unmatchedMask = mask & (~parsingMask);
|
||||
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) +
|
||||
@ -392,6 +391,30 @@ public enum AccessFlag {
|
||||
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<AccessFlag> 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.
|
||||
* <p>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -221,8 +221,9 @@ public abstract sealed class Executable extends AccessibleObject
|
||||
*/
|
||||
@Override
|
||||
public Set<AccessFlag> accessFlags() {
|
||||
return AccessFlag.maskToAccessFlags(getModifiers(),
|
||||
AccessFlag.Location.METHOD);
|
||||
return reflectionFactory.parseAccessFlags(getModifiers(),
|
||||
AccessFlag.Location.METHOD,
|
||||
getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 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
|
||||
@ -218,7 +218,7 @@ class Field extends AccessibleObject implements Member {
|
||||
*/
|
||||
@Override
|
||||
public Set<AccessFlag> accessFlags() {
|
||||
return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.FIELD);
|
||||
return reflectionFactory.parseAccessFlags(getModifiers(), AccessFlag.Location.FIELD, getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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
|
||||
@ -172,8 +172,8 @@ public final class Parameter implements AnnotatedElement {
|
||||
* @since 20
|
||||
*/
|
||||
public Set<AccessFlag> accessFlags() {
|
||||
return AccessFlag.maskToAccessFlags(getModifiers(),
|
||||
AccessFlag.Location.METHOD_PARAMETER);
|
||||
return AccessibleObject.reflectionFactory.parseAccessFlags(getModifiers(),
|
||||
AccessFlag.Location.METHOD_PARAMETER, getDeclaringExecutable().getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -119,6 +119,12 @@ public interface JavaLangAccess {
|
||||
*/
|
||||
<E extends Enum<E>> E[] getEnumConstantsShared(Class<E> klass);
|
||||
|
||||
/**
|
||||
* Returns the big-endian packed minor-major version of the class file
|
||||
* of this class.
|
||||
*/
|
||||
int classFileVersion(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Set current thread's blocker field.
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 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
|
||||
@ -32,15 +32,10 @@ import java.io.ObjectStreamClass;
|
||||
import java.io.ObjectStreamField;
|
||||
import java.io.OptionalDataException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.access.JavaLangReflectAccess;
|
||||
@ -518,6 +513,31 @@ public class ReflectionFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public final Set<AccessFlag> parseAccessFlags(int mask, AccessFlag.Location location, Class<?> classFile) {
|
||||
var cffv = classFileFormatVersion(classFile);
|
||||
return cffv == null ?
|
||||
AccessFlag.maskToAccessFlags(mask, location) :
|
||||
AccessFlag.maskToAccessFlags(mask, location, cffv);
|
||||
}
|
||||
|
||||
private final ClassFileFormatVersion classFileFormatVersion(Class<?> cl) {
|
||||
int raw = SharedSecrets.getJavaLangAccess().classFileVersion(cl);
|
||||
|
||||
int major = raw & 0xFFFF;
|
||||
int minor = raw >>> Character.SIZE;
|
||||
|
||||
assert VM.isSupportedClassFileVersion(major, minor) : major + "." + minor;
|
||||
|
||||
if (major >= ClassFile.JAVA_12_VERSION) {
|
||||
if (minor == 0)
|
||||
return ClassFileFormatVersion.fromMajor(raw);
|
||||
return null; // preview or old preview, fallback to default handling
|
||||
} else if (major == ClassFile.JAVA_1_VERSION) {
|
||||
return minor < 3 ? ClassFileFormatVersion.RELEASE_0 : ClassFileFormatVersion.RELEASE_1;
|
||||
}
|
||||
return ClassFileFormatVersion.fromMajor(major);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Internals only below this point
|
||||
|
||||
@ -59,13 +59,12 @@ public class BasicWriter {
|
||||
}
|
||||
|
||||
protected Set<AccessFlag> maskToAccessFlagsReportUnknown(int mask, AccessFlag.Location location, ClassFileFormatVersion cffv) {
|
||||
// TODO pass cffv to maskToAccessFlags
|
||||
try {
|
||||
return AccessFlag.maskToAccessFlags(mask, location);
|
||||
return AccessFlag.maskToAccessFlags(mask, location, cffv);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
mask &= location.flagsMask(cffv);
|
||||
report("Access Flags: " + ex.getMessage());
|
||||
return AccessFlag.maskToAccessFlags(mask, location);
|
||||
return AccessFlag.maskToAccessFlags(mask, location, cffv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,11 +23,14 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8266670 8293626
|
||||
* @bug 8266670 8293626 8297271
|
||||
* @summary Basic tests of AccessFlag
|
||||
* @run junit BasicAccessFlagTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.lang.reflect.ClassFileFormatVersion;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.EnumSet;
|
||||
@ -36,30 +39,26 @@ import java.util.LinkedHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class BasicAccessFlagTest {
|
||||
public static void main(String... args) throws Exception {
|
||||
testSourceModifiers();
|
||||
testMaskOrdering();
|
||||
testDisjoint();
|
||||
testMaskToAccessFlagsPositive();
|
||||
testLocationsNullHandling();
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify sourceModifier() == true access flags have a
|
||||
* corresponding constant in java.lang.reflect.Modifier.
|
||||
*/
|
||||
private static void testSourceModifiers() throws Exception {
|
||||
@Test
|
||||
public void testSourceModifiers() throws Exception {
|
||||
Class<?> modifierClass = Modifier.class;
|
||||
|
||||
for(AccessFlag accessFlag : AccessFlag.values()) {
|
||||
if (accessFlag.sourceModifier()) {
|
||||
// Check for consistency
|
||||
Field f = modifierClass.getField(accessFlag.name());
|
||||
if (accessFlag.mask() != f.getInt(null) ) {
|
||||
throw new RuntimeException("Unexpected mask for " +
|
||||
accessFlag);
|
||||
}
|
||||
assertEquals(f.getInt(null), accessFlag.mask(), accessFlag + " mask");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,22 +66,22 @@ public class BasicAccessFlagTest {
|
||||
// The mask values of the enum constants must be non-decreasing;
|
||||
// in other words stay the same (for colliding mask values) or go
|
||||
// up.
|
||||
private static void testMaskOrdering() {
|
||||
@Test
|
||||
public void testMaskOrdering() {
|
||||
AccessFlag[] values = AccessFlag.values();
|
||||
for (int i = 1; i < values.length; i++) {
|
||||
AccessFlag left = values[i-1];
|
||||
AccessFlag right = values[i];
|
||||
if (left.mask() > right.mask()) {
|
||||
throw new RuntimeException(left
|
||||
+ "has a greater mask than "
|
||||
+ right);
|
||||
}
|
||||
assertTrue(left.mask() <= right.mask(), () -> left
|
||||
+ "has a greater mask than "
|
||||
+ right);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that if access flags have a matching mask, their locations
|
||||
// are disjoint.
|
||||
private static void testDisjoint() {
|
||||
@Test
|
||||
public void testDisjoint() {
|
||||
// First build the mask -> access flags map...
|
||||
Map<Integer, Set<AccessFlag>> maskToFlags = new LinkedHashMap<>();
|
||||
|
||||
@ -90,7 +89,7 @@ public class BasicAccessFlagTest {
|
||||
Integer mask = accessFlag.mask();
|
||||
Set<AccessFlag> flags = maskToFlags.get(mask);
|
||||
|
||||
if (flags == null ) {
|
||||
if (flags == null) {
|
||||
flags = new HashSet<>();
|
||||
flags.add(accessFlag);
|
||||
maskToFlags.put(mask, flags);
|
||||
@ -135,7 +134,8 @@ public class BasicAccessFlagTest {
|
||||
|
||||
// For each access flag, make sure it is recognized on every kind
|
||||
// of location it can apply to
|
||||
private static void testMaskToAccessFlagsPositive() {
|
||||
@Test
|
||||
public void testMaskToAccessFlagsPositive() {
|
||||
for (var accessFlag : AccessFlag.values()) {
|
||||
Set<AccessFlag> expectedSet = EnumSet.of(accessFlag);
|
||||
for (var location : accessFlag.locations()) {
|
||||
@ -146,17 +146,43 @@ public class BasicAccessFlagTest {
|
||||
accessFlag + ", " + location);
|
||||
}
|
||||
}
|
||||
for (var cffv : ClassFileFormatVersion.values()) {
|
||||
for (var location : accessFlag.locations(cffv)) {
|
||||
Set<AccessFlag> computedSet =
|
||||
AccessFlag.maskToAccessFlags(accessFlag.mask(), location, cffv);
|
||||
if (!expectedSet.equals(computedSet)) {
|
||||
throw new RuntimeException("Bad set computation on " +
|
||||
accessFlag + ", " + location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(Set.of(AccessFlag.STRICT), AccessFlag.maskToAccessFlags(Modifier.STRICT, AccessFlag.Location.METHOD, ClassFileFormatVersion.RELEASE_8));
|
||||
}
|
||||
|
||||
private static void testLocationsNullHandling() {
|
||||
for (var flag : AccessFlag.values() ) {
|
||||
try {
|
||||
flag.locations(null);
|
||||
throw new RuntimeException("Did not get NPE on " + flag + ".location(null)");
|
||||
} catch (NullPointerException npe ) {
|
||||
; // Expected
|
||||
}
|
||||
@Test
|
||||
public void testMaskToAccessFlagsNegative() {
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(Modifier.STRICT, AccessFlag.Location.METHOD));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(Modifier.STRICT, AccessFlag.Location.METHOD, ClassFileFormatVersion.RELEASE_17));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(Modifier.STRICT, AccessFlag.Location.METHOD, ClassFileFormatVersion.RELEASE_1));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(Modifier.PRIVATE, AccessFlag.Location.CLASS));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_MODULE, AccessFlag.Location.CLASS, ClassFileFormatVersion.RELEASE_8));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_ANNOTATION, AccessFlag.Location.CLASS, ClassFileFormatVersion.RELEASE_4));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_ENUM, AccessFlag.Location.FIELD, ClassFileFormatVersion.RELEASE_4));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_SYNTHETIC, AccessFlag.Location.INNER_CLASS, ClassFileFormatVersion.RELEASE_4));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_PUBLIC, AccessFlag.Location.INNER_CLASS, ClassFileFormatVersion.RELEASE_0));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_MANDATED, AccessFlag.Location.METHOD_PARAMETER, ClassFileFormatVersion.RELEASE_7));
|
||||
assertThrows(IllegalArgumentException.class, () -> AccessFlag.maskToAccessFlags(ClassFile.ACC_MANDATED, AccessFlag.Location.MODULE, ClassFileFormatVersion.RELEASE_7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationsNullHandling() {
|
||||
for (var flag : AccessFlag.values()) {
|
||||
assertThrows(NullPointerException.class, () -> flag.locations(null));
|
||||
}
|
||||
|
||||
for (var location : AccessFlag.Location.values()) {
|
||||
assertThrows(NullPointerException.class, () -> location.flags(null));
|
||||
}
|
||||
|
||||
for (var location : AccessFlag.Location.values()) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user