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 }