1 /*
2 * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.internal.reflect;
27
28 import java.lang.reflect.AccessFlag;
29 import java.lang.reflect.ClassFileFormatVersion;
30 import java.util.AbstractSet;
31 import java.util.Collection;
32 import java.util.Iterator;
33 import java.util.NoSuchElementException;
34 import java.util.Objects;
35 import java.util.Set;
36 import java.util.function.Consumer;
37 import java.util.function.Predicate;
38
39 import jdk.internal.vm.annotation.Stable;
40
41 import static java.lang.reflect.AccessFlag.*;
42
43 /// An access flag set is an optimized immutable set backed by an integer mask
44 /// and a "definition" that interprets each bit in that mask.
45 /// This set has a well-defined iteration order from the least significant bit
46 /// to the most significant bit, is null-hostile, and throws UOE for any
47 /// modification operation, like the other unmodifiable collections.
48 ///
49 /// The "definition" is an array of 16 entries, the maximum number of different
50 /// access flags a classfile `u2 access_flags` field can represent.
51 /// Given an access flag bit, we can look up the array entry corresponding to
52 /// its bit position to find the corresponding AccessFlag.
53 ///
54 /// The definition can vary by location and class file format of the
55 /// access_flags field.
56 /// If a bit position does not have an access flag defined, its corresponding
57 /// array entry is `null`.
58 public final class AccessFlagSet extends AbstractSet<AccessFlag> {
59
60 public static final @Stable AccessFlag[]
61 CLASS_FLAGS = createDefinition(PUBLIC, FINAL, SUPER, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM, MODULE),
62 FIELD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, VOLATILE, TRANSIENT, SYNTHETIC, ENUM),
63 METHOD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, BRIDGE, VARARGS, NATIVE, ABSTRACT, STRICT, SYNTHETIC),
64 INNER_CLASS_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM),
65 METHOD_PARAMETER_FLAGS = createDefinition(FINAL, SYNTHETIC, MANDATED),
66 MODULE_FLAGS = createDefinition(OPEN, SYNTHETIC, MANDATED),
67 MODULE_REQUIRES_FLAGS = createDefinition(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED),
68 MODULE_EXPORTS_FLAGS = createDefinition(SYNTHETIC, MANDATED),
69 MODULE_OPENS_FLAGS = createDefinition(SYNTHETIC, MANDATED);
70
71 /// Finds a definition that works for access flags for a classfile location for the latest class file format.
72 /// @see #findDefinition(Location, ClassFileFormatVersion)
73 public static AccessFlag[] findDefinition(Location location) {
74 return findDefinition(location, ClassFileFormatVersion.latest());
75 }
76
77 /// Finds a definition that works for access flags for a classfile location for the given class file format.
78 /// The definition may define extraneous flags not present in the given class file format, so do not derive
79 /// the mask of defined flags from this definition.
80 public static AccessFlag[] findDefinition(Location location, ClassFileFormatVersion cffv) {
81 Objects.requireNonNull(cffv);
82 // implicit null check location
83 return switch (location) {
84 case CLASS -> CLASS_FLAGS;
85 case FIELD -> FIELD_FLAGS;
86 case METHOD -> METHOD_FLAGS;
87 case INNER_CLASS -> INNER_CLASS_FLAGS;
88 case METHOD_PARAMETER -> METHOD_PARAMETER_FLAGS;
89 case MODULE -> MODULE_FLAGS;
90 case MODULE_REQUIRES -> MODULE_REQUIRES_FLAGS;
91 case MODULE_EXPORTS -> MODULE_EXPORTS_FLAGS;
92 case MODULE_OPENS -> MODULE_OPENS_FLAGS;
93 };
94 }
95
96 /// Converts an array of defined access flags to a proper "definition".
97 /// Users no longer have to explicitly assign access flags to their correct bit positions.
98 static AccessFlag[] createDefinition(AccessFlag... known) {
99 var ret = new AccessFlag[Character.SIZE];
100 for (var flag : known) {
101 var mask = flag.mask();
102 int pos = Integer.numberOfTrailingZeros(mask);
103 assert ret[pos] == null : ret[pos] + " duplicates " + flag;
104 ret[pos] = flag;
105 }
106 return ret;
107 }
108
109 /// Creates an access flag set with a mask where every set bit corresponds
110 /// to an access flag in the definition.
111 /// The mask must be validated: if any bit in the mask does not have a
112 /// corresponding access flag, this set will be corrupted.
113 public static Set<AccessFlag> ofValidated(AccessFlag[] definition, int mask) {
114 return new AccessFlagSet(definition, mask);
115 }
116
117 // Minimal mask validation for a definition.
118 // In practice, more bits are usually rejected due to context like class file versions.
119 private static int undefinedMask(AccessFlag[] definition, int mask) {
120 assert definition.length == Character.SIZE;
121 int definedMask = 0;
122 for (int i = 0; i < Character.SIZE; i++) {
123 if (definition[i] != null) {
124 definedMask |= 1 << i;
125 }
126 }
127 return mask & ~definedMask;
128 }
129
130 private final @Stable AccessFlag[] definition;
131 private final int mask;
132
133 // all mutating methods throw UnsupportedOperationException
134 @Override public boolean add(AccessFlag e) { throw uoe(); }
135 @Override public boolean addAll(Collection<? extends AccessFlag> c) { throw uoe(); }
136 @Override public void clear() { throw uoe(); }
137 @Override public boolean remove(Object o) { throw uoe(); }
138 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
139 @Override public boolean removeIf(Predicate<? super AccessFlag> filter) { throw uoe(); }
140 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
141 private static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
142
143 private AccessFlagSet(AccessFlag[] definition, int mask) {
144 assert undefinedMask(definition, mask) == 0 : mask;
145 this.definition = definition;
146 this.mask = mask;
147 }
148
149 @Override
150 public Iterator<AccessFlag> iterator() {
151 return new AccessFlagIterator(definition, mask);
152 }
153
154 @Override
155 public void forEach(Consumer<? super AccessFlag> action) {
156 Objects.requireNonNull(action); // in case of empty
157 for (int i = 0; i < Character.SIZE; i++) {
158 if ((mask & (1 << i)) != 0) {
159 action.accept(definition[i]);
160 }
161 }
162 }
163
164 private static final class AccessFlagIterator implements Iterator<AccessFlag> {
165 private final @Stable AccessFlag[] definition;
166 private int remainingMask;
167
168 private AccessFlagIterator(AccessFlag[] definition, int remainingMask) {
169 this.definition = definition;
170 this.remainingMask = remainingMask;
171 }
172
173 @Override
174 public boolean hasNext() {
175 return remainingMask != 0;
176 }
177
178 @Override
179 public AccessFlag next() {
180 int flagBit = Integer.lowestOneBit(remainingMask);
181 if (flagBit == 0) {
182 throw new NoSuchElementException();
183 }
184 remainingMask &= ~flagBit;
185 return definition[Integer.numberOfTrailingZeros(flagBit)];
186 }
187 }
188
189 @Override
190 public int size() {
191 return Integer.bitCount(mask);
192 }
193
194 @Override
195 public boolean contains(Object o) {
196 if (Objects.requireNonNull(o) instanceof AccessFlag flag) {
197 int bit = flag.mask();
198 return (bit & mask) != 0 && definition[Integer.numberOfTrailingZeros(bit)] == flag;
199 }
200 return false;
201 }
202
203 @Override
204 public boolean isEmpty() {
205 return mask == 0;
206 }
207 }