From dcffd9d9ac1ad1ed814b5bb45222a25621eeccda Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 30 Apr 2025 00:39:02 +0000 Subject: [PATCH] 8347471: Provide valid flags and mask in AccessFlag.Location Reviewed-by: rriggs --- .../classes/java/lang/reflect/AccessFlag.java | 665 +++++++++++------- .../classes/java/lang/reflect/Modifier.java | 8 + .../AccessFlag/BasicAccessFlagTest.java | 17 +- .../AccessFlag/ClassAccessFlagTest.java | 52 +- .../AccessFlag/FieldAccessFlagTest.java | 31 +- .../AccessFlag/MethodAccessFlagTest.java | 41 +- .../AccessFlag/StrictAccessFlagTest.java | 23 +- .../AccessFlag/VersionedLocationsTest.java | 55 +- test/jdk/java/util/Collection/MOAT.java | 6 +- 9 files changed, 578 insertions(+), 320 deletions(-) diff --git a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java index bc6b20be9a8..bb58c440688 100644 --- a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java +++ b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,12 +25,31 @@ package java.lang.reflect; -import java.util.Collections; -import java.util.Objects; +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.Function; -import static java.util.Map.entry; +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, @@ -93,26 +112,14 @@ import static java.util.Map.entry; */ @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; - } - }), + 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 @@ -120,14 +127,7 @@ public enum AccessFlag { * 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; - } - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_PROTECTED}, corresponding to the @@ -135,14 +135,7 @@ public enum AccessFlag { * 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; - } - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_STATIC}, corresponding to the source @@ -150,13 +143,7 @@ public enum AccessFlag { * {@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;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_FINAL}, corresponding to the source @@ -165,18 +152,8 @@ public enum AccessFlag { */ 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; - } - } - }), + 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 @@ -186,21 +163,15 @@ 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, null), + 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, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_MODULE: - Location.EMPTY_SET;} - }), + 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 @@ -208,20 +179,14 @@ public enum AccessFlag { * @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;} - }), + 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, null), + SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Location.SET_METHOD, List.of()), /** * The module requires flag {@code ACC_STATIC_PHASE} with a mask @@ -229,20 +194,14 @@ public enum AccessFlag { * @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;} - }), + 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, null), + VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD, List.of()), /** * The access flag {@code ACC_BRIDGE} with a mask value of @@ -250,41 +209,29 @@ public enum AccessFlag { * @see Method#isBridge() */ BRIDGE(Modifier.BRIDGE, 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;} - }), + 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, null), + 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}. + * {@value "0x%04x" Modifier#VARARGS}. * @see Executable#isVarArgs() */ VARARGS(Modifier.VARARGS, 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;} - }), + 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, null), + NATIVE(Modifier.NATIVE, true, Location.SET_METHOD, List.of()), /** * The access flag {@code ACC_INTERFACE} with a mask value of @@ -292,13 +239,7 @@ public enum AccessFlag { * @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;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_CLASS))), /** * The access flag {@code ACC_ABSTRACT}, corresponding to the @@ -307,13 +248,7 @@ public enum AccessFlag { */ 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;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_CLASS_METHOD))), /** * The access flag {@code ACC_STRICT}, corresponding to the source @@ -326,14 +261,8 @@ public enum AccessFlag { * 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;} - }), + 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 @@ -343,21 +272,9 @@ public enum AccessFlag { * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC */ SYNTHETIC(Modifier.SYNTHETIC, 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; - }; - } - } - }), + 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 @@ -365,13 +282,7 @@ public enum AccessFlag { * @see Class#isAnnotation() */ ANNOTATION(Modifier.ANNOTATION, 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;} - }), + List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_ENUM} with a mask value of @@ -379,44 +290,22 @@ public enum AccessFlag { * @see Class#isEnum() */ ENUM(Modifier.ENUM, 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;} - }), + 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, - 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; - } - } - }), + 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, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_CLASS: - Location.EMPTY_SET;} - }) + List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), ; // May want to override toString for a different enum constant -> @@ -425,31 +314,31 @@ public enum AccessFlag { private final int mask; private final boolean sourceModifier; - // Intentionally using Set rather than EnumSet since EnumSet is - // mutable. + // immutable private final Set locations; - // Lambda to implement locations(ClassFileFormatVersion cffv) - private final Function> cffvToLocations; + // historical locations up to a given version; immutable + private final List>> historicalLocations; private AccessFlag(int mask, boolean sourceModifier, Set locations, - Function> cffvToLocations) { + List>> historicalLocations) { this.mask = mask; this.sourceModifier = sourceModifier; this.locations = locations; - this.cffvToLocations = cffvToLocations; + this.historicalLocations = Location.ensureHistoryOrdered(historicalLocations); } /** - * {@return the corresponding integer mask for the access flag} + * {@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 the flag has a directly corresponding + * {@return whether or not this flag has a directly corresponding * modifier in the Java programming language} */ public boolean sourceModifier() { @@ -457,26 +346,28 @@ public enum AccessFlag { } /** - * {@return kinds of constructs the flag can be applied to in the - * latest class file format version} + * {@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 kinds of constructs the flag can be applied to in the - * given class file format version} + * {@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) { - Objects.requireNonNull(cffv); - if (cffvToLocations == null) { - return locations; - } else { - return cffvToLocations.apply(cffv); - } + return Location.findInHistory(locations, historicalLocations, cffv); } /** @@ -489,84 +380,160 @@ public enum AccessFlag { * 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) { + var definition = findDefinition(location); + int flagsMask = location.flagsMask(); + int parsingMask = location == Location.METHOD ? flagsMask | ACC_STRICT : flagsMask; // flagMask lacks strictfp + int unmatchedMask = mask & (~parsingMask); + if (unmatchedMask != 0) { throw new IllegalArgumentException("Unmatched bit position 0x" + - Integer.toHexString(mask) + - " for location " + location); + Integer.toHexString(unmatchedMask) + + " for location " + location); } - return Collections.unmodifiableSet(result); + return new AccessFlagSet(definition, mask); } /** - * 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 + * 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. - * @jvms 4.1 The ClassFile Structure + * + * @see Class#accessFlags() + * @see ClassModel#flags() + * @see Modifier#classModifiers() + * @see Modifier#interfaceModifiers() + * @jvms 4.1 The {@code ClassFile} Structure */ - CLASS, + 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, + 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, + 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. - * @jvms 4.7.6 The InnerClasses Attribute + * + * @see Class#accessFlags() + * @see InnerClassInfo#flags() + * @see Modifier#classModifiers() + * @see Modifier#interfaceModifiers() + * @jvms 4.7.6 The {@code InnerClasses} Attribute */ - INNER_CLASS, + 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. - * @jvms 4.7.24 The MethodParameters Attribute + * + * @see Parameter#accessFlags() + * @see MethodParameterInfo#flags() + * @see Modifier#parameterModifiers() + * @jvms 4.7.24 The {@code MethodParameters} Attribute */ - METHOD_PARAMETER, + METHOD_PARAMETER(ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_7, 0))), // did not exist /** - * Module location - * @jvms 4.7.25 The Module Attribute + * Module location. + * + * @see ModuleDescriptor#accessFlags() + * @see ModuleAttribute#moduleFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE, + MODULE(ACC_OPEN | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module requires location - * @jvms 4.7.25 The Module Attribute + * Module requires location. + * + * @see ModuleDescriptor.Requires#accessFlags() + * @see ModuleRequireInfo#requiresFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_REQUIRES, + MODULE_REQUIRES(ACC_TRANSITIVE | ACC_STATIC_PHASE | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module exports location - * @jvms 4.7.25 The Module Attribute + * Module exports location. + * + * @see ModuleDescriptor.Exports#accessFlags() + * @see ModuleExportInfo#exportsFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_EXPORTS, + MODULE_EXPORTS(ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module opens location - * @jvms 4.7.25 The Module Attribute + * Module opens location. + * + * @see ModuleDescriptor.Opens#accessFlags() + * @see ModuleOpenInfo#opensFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_OPENS; + 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(); @@ -593,20 +560,18 @@ public enum AccessFlag { 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 = + 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 7 + // Added as an access flag in 5.0 Set.of(CLASS, FIELD, METHOD, INNER_CLASS, METHOD_PARAMETER, // Added in 8 @@ -618,37 +583,227 @@ public enum AccessFlag { // 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 class LocationToFlags { - private static 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))); + 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; + } } } diff --git a/src/java.base/share/classes/java/lang/reflect/Modifier.java b/src/java.base/share/classes/java/lang/reflect/Modifier.java index 1b7bc3bf65b..624f32b4e04 100644 --- a/src/java.base/share/classes/java/lang/reflect/Modifier.java +++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java @@ -448,6 +448,8 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a class. * + * @see AccessFlag.Location#CLASS + * @see AccessFlag.Location#INNER_CLASS * @jls 8.1.1 Class Modifiers * @since 1.7 */ @@ -461,6 +463,8 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to an interface. * + * @see AccessFlag.Location#CLASS + * @see AccessFlag.Location#INNER_CLASS * @jls 9.1.1 Interface Modifiers * @since 1.7 */ @@ -474,6 +478,7 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a constructor. * + * @see AccessFlag.Location#METHOD * @jls 8.8.3 Constructor Modifiers * @since 1.7 */ @@ -487,6 +492,7 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a method. * + * @see AccessFlag.Location#METHOD * @jls 8.4.3 Method Modifiers * @since 1.7 */ @@ -500,6 +506,7 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a field. * + * @see AccessFlag.Location#FIELD * @jls 8.3.1 Field Modifiers * @since 1.7 */ @@ -513,6 +520,7 @@ public final class Modifier { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a parameter. * + * @see AccessFlag.Location#METHOD_PARAMETER * @jls 8.4.1 Formal Parameters * @since 1.8 */ diff --git a/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java index 12eeeb45c6e..d87f60a0a1a 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -158,5 +158,20 @@ public class BasicAccessFlagTest { ; // Expected } } + + for (var location : AccessFlag.Location.values()) { + try { + location.flags(null); + throw new RuntimeException("Did not get NPE on " + location + ".flags(null)"); + } catch (NullPointerException npe ) { + ; // Expected + } + try { + location.flagsMask(null); + throw new RuntimeException("Did not get NPE on " + location + ".flagsMask(null)"); + } catch (NullPointerException npe ) { + ; // Expected + } + } } } diff --git a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java index 10ebfd6aff2..1c80c29dc9b 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -31,6 +31,8 @@ import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; +import static java.lang.reflect.AccessFlag.*; + /* * Class access flags that can directly or indirectly declared in * source include: @@ -47,7 +49,7 @@ import java.util.*; * file. Therefore, this test does not attempt to probe the setting of * that access flag. */ -@ExpectedClassFlags("[PUBLIC, FINAL, SUPER]") +@ExpectedClassFlags({PUBLIC, FINAL, SUPER}) public final class ClassAccessFlagTest { public static void main(String... args) { // Top-level and auxiliary classes; i.e. non-inner classes @@ -76,10 +78,12 @@ public final class ClassAccessFlagTest { ExpectedClassFlags expected = clazz.getAnnotation(ExpectedClassFlags.class); if (expected != null) { - String actual = clazz.accessFlags().toString(); - if (!expected.value().equals(actual)) { + Set base = EnumSet.noneOf(AccessFlag.class); + Collections.addAll(base, expected.value()); + Set actual = clazz.accessFlags(); + if (!base.equals(actual)) { throw new RuntimeException("On " + clazz + - " expected " + expected.value() + + " expected " + base + " got " + actual); } } @@ -98,7 +102,7 @@ public final class ClassAccessFlagTest { void.class // same access flag rules }; - var expected = Set.of(AccessFlag.PUBLIC, + var expected = Set.of(PUBLIC, AccessFlag.FINAL, AccessFlag.ABSTRACT); @@ -127,8 +131,8 @@ public final class ClassAccessFlagTest { for (var accessClass : accessClasses) { AccessFlag accessLevel; var flags = accessClass.accessFlags(); - if (flags.contains(AccessFlag.PUBLIC)) - accessLevel = AccessFlag.PUBLIC; + if (flags.contains(PUBLIC)) + accessLevel = PUBLIC; else if (flags.contains(AccessFlag.PROTECTED)) accessLevel = AccessFlag.PROTECTED; else if (flags.contains(AccessFlag.PRIVATE)) @@ -145,7 +149,7 @@ public final class ClassAccessFlagTest { } } else { if (containsAny(arrayClass.accessFlags(), - Set.of(AccessFlag.PUBLIC, + Set.of(PUBLIC, AccessFlag.PROTECTED, AccessFlag.PRIVATE))) { throw new RuntimeException("Unexpected access flags on " + @@ -161,32 +165,32 @@ public final class ClassAccessFlagTest { // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, // SYNTHETIC, ANNOTATION, ENUM. - @ExpectedClassFlags("[PUBLIC, STATIC, INTERFACE, ABSTRACT]") + @ExpectedClassFlags({PUBLIC, STATIC, INTERFACE, ABSTRACT}) public interface PublicInterface {} - @ExpectedClassFlags("[PROTECTED, STATIC, INTERFACE, ABSTRACT]") + @ExpectedClassFlags({PROTECTED, STATIC, INTERFACE, ABSTRACT}) protected interface ProtectedInterface {} - @ExpectedClassFlags("[PRIVATE, STATIC, INTERFACE, ABSTRACT]") + @ExpectedClassFlags({PRIVATE, STATIC, INTERFACE, ABSTRACT}) private interface PrivateInterface {} - @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]") + @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT}) /*package*/ interface PackageInterface {} - @ExpectedClassFlags("[FINAL]") + @ExpectedClassFlags({FINAL}) /*package*/ final class TestFinalClass {} - @ExpectedClassFlags("[ABSTRACT]") + @ExpectedClassFlags({ABSTRACT}) /*package*/ abstract class TestAbstractClass {} - @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT, ANNOTATION]") + @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT, ANNOTATION}) /*package*/ @interface TestMarkerAnnotation {} - @ExpectedClassFlags("[PUBLIC, STATIC, FINAL, ENUM]") + @ExpectedClassFlags({PUBLIC, STATIC, FINAL, ENUM}) public enum MetaSynVar { QUUX; } // Is there is at least one special enum constant, the enum class // itself is implicitly abstract rather than final. - @ExpectedClassFlags("[PROTECTED, STATIC, ABSTRACT, ENUM]") + @ExpectedClassFlags({PROTECTED, STATIC, ABSTRACT, ENUM}) protected enum MetaSynVar2 { WOMBAT{ @Override @@ -195,24 +199,24 @@ public final class ClassAccessFlagTest { public abstract int foo(); } - @ExpectedClassFlags("[PRIVATE, ABSTRACT]") + @ExpectedClassFlags({PRIVATE, ABSTRACT}) private abstract class Foo {} - @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]") + @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT}) interface StaticTestInterface {} } @Retention(RetentionPolicy.RUNTIME) -@ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]") +@ExpectedClassFlags({INTERFACE, ABSTRACT, ANNOTATION}) @interface ExpectedClassFlags { - String value(); + AccessFlag[] value(); } -@ExpectedClassFlags("[INTERFACE, ABSTRACT]") +@ExpectedClassFlags({INTERFACE, ABSTRACT}) interface TestInterface {} -@ExpectedClassFlags("[FINAL, SUPER, ENUM]") +@ExpectedClassFlags({FINAL, SUPER, ENUM}) enum TestOuterEnum { INSTANCE; } diff --git a/test/jdk/java/lang/reflect/AccessFlag/FieldAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/FieldAccessFlagTest.java index edd35f7b6a3..ab12bc75e16 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/FieldAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/FieldAccessFlagTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -29,6 +29,11 @@ import java.lang.annotation.*; import java.lang.reflect.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import static java.lang.reflect.AccessFlag.*; /* * Field modifiers include: @@ -54,39 +59,41 @@ public class FieldAccessFlagTest { ExpectedFieldFlags expected = field.getAnnotation(ExpectedFieldFlags.class); if (expected != null) { - String actual = field.accessFlags().toString(); - if (!expected.value().equals(actual)) { + Set base = EnumSet.noneOf(AccessFlag.class); + Collections.addAll(base, expected.value()); + Set actual = field.accessFlags(); + if (!base.equals(actual)) { throw new RuntimeException("On " + field + - " expected " + expected.value() + - " got " + actual); + " expected " + base + + " got " + actual); } } } // Fields - @ExpectedFieldFlags("[PUBLIC, STATIC, FINAL]") + @ExpectedFieldFlags({PUBLIC, STATIC, FINAL}) public static final String f1 = "foo"; - @ExpectedFieldFlags("[PRIVATE, VOLATILE, TRANSIENT]") + @ExpectedFieldFlags({PRIVATE, VOLATILE, TRANSIENT}) private volatile transient String secret = "xxyzzy"; - @ExpectedFieldFlags("[PROTECTED]") + @ExpectedFieldFlags({PROTECTED}) protected String meadow = ""; // Enum constant should have the enum access flag set static enum MetaSynVar { - @ExpectedFieldFlags("[PUBLIC, STATIC, FINAL, ENUM]") + @ExpectedFieldFlags({PUBLIC, STATIC, FINAL, ENUM}) FOO, - @ExpectedFieldFlags("[PUBLIC, STATIC, FINAL, ENUM]") + @ExpectedFieldFlags({PUBLIC, STATIC, FINAL, ENUM}) BAR; - @ExpectedFieldFlags("[PRIVATE]") // no "ENUM" + @ExpectedFieldFlags({PRIVATE}) // no "ENUM" private int field = 0; } @Retention(RetentionPolicy.RUNTIME) private @interface ExpectedFieldFlags { - String value(); + AccessFlag[] value(); } } diff --git a/test/jdk/java/lang/reflect/AccessFlag/MethodAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/MethodAccessFlagTest.java index a6f65aa31e6..91296727f96 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/MethodAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/MethodAccessFlagTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -34,6 +34,11 @@ import java.lang.annotation.*; import java.lang.reflect.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import static java.lang.reflect.AccessFlag.*; /* * Method modifiers include: @@ -49,7 +54,7 @@ import java.lang.reflect.*; * Method parameters can be final, synthetic, and mandated. */ public abstract class MethodAccessFlagTest { - @ExpectedMethodFlags("[PUBLIC, STATIC, VARARGS]") + @ExpectedMethodFlags({PUBLIC, STATIC, VARARGS}) public static void main(String... args) { for (var ctor : MethodAccessFlagTest.class.getDeclaredConstructors()) { @@ -136,11 +141,17 @@ public abstract class MethodAccessFlagTest { } private static void checkExecutable(Executable method) { - ExpectedMethodFlags emf = + ExpectedMethodFlags expected = method.getAnnotation(ExpectedMethodFlags.class); - if (emf != null) { - String actual = method.accessFlags().toString(); - checkString(method.toString(), emf.value(), actual); + if (expected != null) { + Set base = EnumSet.noneOf(AccessFlag.class); + Collections.addAll(base, expected.value()); + Set actual = method.accessFlags(); + if (!base.equals(actual)) { + throw new RuntimeException("On " + method + + " expected " + base + + " got " + actual); + } } } @@ -155,33 +166,33 @@ public abstract class MethodAccessFlagTest { } // Constructors - @ExpectedMethodFlags("[PUBLIC]") + @ExpectedMethodFlags({PUBLIC}) public MethodAccessFlagTest() {} - @ExpectedMethodFlags("[PROTECTED]") + @ExpectedMethodFlags({PROTECTED}) protected MethodAccessFlagTest(int i) {super();} - @ExpectedMethodFlags("[PRIVATE]") + @ExpectedMethodFlags({PRIVATE}) private MethodAccessFlagTest(String s) {super();} // Methods - @ExpectedMethodFlags("[PROTECTED, SYNCHRONIZED]") + @ExpectedMethodFlags({PROTECTED, SYNCHRONIZED}) protected synchronized void m0() {} - @ExpectedMethodFlags("[PRIVATE]") + @ExpectedMethodFlags({PRIVATE}) private void m1() {} - @ExpectedMethodFlags("[ABSTRACT]") + @ExpectedMethodFlags({ABSTRACT}) abstract void m2(); - @ExpectedMethodFlags("[PUBLIC, FINAL]") + @ExpectedMethodFlags({PUBLIC, FINAL}) public final void m3() {} - @ExpectedMethodFlags("[NATIVE]") + @ExpectedMethodFlags({NATIVE}) native void m4(); @Retention(RetentionPolicy.RUNTIME) private @interface ExpectedMethodFlags { - String value(); + AccessFlag[] value(); } } diff --git a/test/jdk/java/lang/reflect/AccessFlag/StrictAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/StrictAccessFlagTest.java index 53a133b57b8..1c2d6d01df0 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/StrictAccessFlagTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/StrictAccessFlagTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -35,6 +35,11 @@ import java.lang.annotation.*; import java.lang.reflect.*; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import static java.lang.reflect.AccessFlag.*; /* * Test expected value of ACC_STRICT access flag. @@ -42,7 +47,7 @@ import java.lang.reflect.*; // Declaring the class strictfp implicitly sets ACC_STRICT on all its // methods and constructors. public strictfp class StrictAccessFlagTest { - @ExpectedFlags("[PUBLIC, STATIC, VARARGS, STRICT]") + @ExpectedFlags({PUBLIC, STATIC, VARARGS, STRICT}) public static void main(String... args) { for (var ctor : StrictAccessFlagTest.class.getDeclaredConstructors()) { @@ -59,21 +64,23 @@ public strictfp class StrictAccessFlagTest { ExpectedFlags expected = method.getAnnotation(ExpectedFlags.class); if (expected != null) { - String actual = method.accessFlags().toString(); - if (!expected.value().equals(actual)) { + Set base = EnumSet.noneOf(AccessFlag.class); + Collections.addAll(base, expected.value()); + Set actual = method.accessFlags(); + if (!base.equals(actual)) { throw new RuntimeException("On " + method + - " expected " + expected.value() + - " got " + actual); + " expected " + base + + " got " + actual); } } } // Constructor - @ExpectedFlags("[PUBLIC, STRICT]") + @ExpectedFlags({PUBLIC, STRICT}) public StrictAccessFlagTest() {} @Retention(RetentionPolicy.RUNTIME) private @interface ExpectedFlags { - String value(); + AccessFlag[] value(); } } diff --git a/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java b/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java index 353edb7852a..29130cbec38 100644 --- a/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java +++ b/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -24,7 +24,8 @@ /* * @test * @bug 8289106 8293627 - * @summary Tests of AccessFlag.locations(ClassFileFormatVersion) + * @summary Tests of AccessFlag.locations(ClassFileFormatVersion) and + * accessors on AccessFlag.Location */ import java.lang.reflect.AccessFlag; @@ -79,6 +80,8 @@ public class VersionedLocationsTest { testSynthetic(); testStrict(); testLatestMatch(); + testFlagVersionConsistency(); + testLocationMaskFlagConsistency(); } /** @@ -241,12 +244,13 @@ public class VersionedLocationsTest { private static void testSynthetic() { for (var cffv : ClassFileFormatVersion.values()) { Set expected; - if (cffv.compareTo(ClassFileFormatVersion.RELEASE_6) <= 0) { + if (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) < 0) { expected = Set.of(); } else { expected = switch(cffv) { - case RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, + case RELEASE_5, RELEASE_6, + RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS); case RELEASE_8 -> Set.of(Location.CLASS, Location.FIELD, @@ -285,4 +289,47 @@ public class VersionedLocationsTest { } } + private static void testFlagVersionConsistency() { + for (var flag : AccessFlag.values()) { + for (var location : AccessFlag.Location.values()) { + if (location.flags().contains(flag) != flag.locations().contains(location)) { + throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + + "flag %s and location %s are inconsistent for the latest version")); + } + } + } + for (var cffv : ClassFileFormatVersion.values()) { + for (var flag : AccessFlag.values()) { + for (var location : AccessFlag.Location.values()) { + if (location.flags(cffv).contains(flag) != flag.locations(cffv).contains(location)) { + throw new RuntimeException(String.format("AccessFlag and Location inconsistency:" + + "flag %s and location %s are inconsistent for class file version %s")); + } + } + } + } + } + + private static void testLocationMaskFlagConsistency() { + for (var location : AccessFlag.Location.values()) { + if (!flagsAndMaskMatch(location.flags(), location.flagsMask())) { + throw new RuntimeException(String.format("Flags and mask mismatch for %s", location)); + } + for (var cffv : ClassFileFormatVersion.values()) { + if (!flagsAndMaskMatch(location.flags(cffv), location.flagsMask(cffv))) { + throw new RuntimeException(String.format("Flags and mask mismatch for %s in %s", location, cffv)); + } + } + } + } + + private static boolean flagsAndMaskMatch(Set flags, int mask) { + for (var flag : flags) { + int bit = flag.mask(); + if (((mask & bit) == 0)) + return false; + mask &= ~bit; + } + return mask == 0; + } } diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java index 1a4e5503f63..1c997a4b26a 100644 --- a/test/jdk/java/util/Collection/MOAT.java +++ b/test/jdk/java/util/Collection/MOAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -138,6 +138,8 @@ public class MOAT { // Unmodifiable wrappers testImmutableSet(unmodifiableSet(new HashSet<>(Arrays.asList(1,2,3))), 99); + testImmutableSet(AccessFlag.maskToAccessFlags(0, AccessFlag.Location.CLASS), AccessFlag.ABSTRACT); + testImmutableSet(AccessFlag.maskToAccessFlags(Modifier.PUBLIC | Modifier.STATIC | Modifier.SYNCHRONIZED, AccessFlag.Location.METHOD), AccessFlag.ABSTRACT); testImmutableList(unmodifiableList(Arrays.asList(1,2,3))); testImmutableMap(unmodifiableMap(Collections.singletonMap(1,2))); testImmutableSeqColl(unmodifiableSequencedCollection(Arrays.asList(1,2,3)), 99); @@ -478,6 +480,8 @@ public class MOAT { () -> c.remove(first), () -> c.removeAll(singleton(first)), () -> c.retainAll(emptyList())); + } else { + testEmptyIterator(c.iterator()); } testForEachMatch(c); testSpliteratorMatch(c);