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 java.lang.reflect;
27
28 import java.lang.classfile.ClassModel;
29 import java.lang.classfile.FieldModel;
30 import java.lang.classfile.MethodModel;
31 import java.lang.classfile.attribute.InnerClassInfo;
32 import java.lang.classfile.attribute.MethodParameterInfo;
33 import java.lang.classfile.attribute.ModuleAttribute;
34 import java.lang.classfile.attribute.ModuleExportInfo;
35 import java.lang.classfile.attribute.ModuleOpenInfo;
36 import java.lang.classfile.attribute.ModuleRequireInfo;
37 import java.lang.module.ModuleDescriptor;
38 import java.util.AbstractSet;
39 import java.util.Collection;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.NoSuchElementException;
44 import java.util.Objects;
45 import java.util.Set;
46 import java.util.function.Consumer;
47 import java.util.function.Predicate;
48
49 import jdk.internal.vm.annotation.Stable;
50
51 import static java.lang.classfile.ClassFile.*;
52 import static java.lang.reflect.ClassFileFormatVersion.*;
53
54 /**
55 * Represents a JVM access or module-related flag on a runtime member,
56 * such as a {@linkplain Class class}, {@linkplain Field field}, or
57 * {@linkplain Executable method}.
58 *
59 * <P>JVM access and module-related flags are related to, but distinct
60 * from Java language {@linkplain Modifier modifiers}. Some modifiers
61 * and access flags have a one-to-one correspondence, such as {@code
62 * public}. In other cases, some language-level modifiers do
63 * <em>not</em> have an access flag, such as {@code sealed} (JVMS
64 * {@jvms 4.7.31}) and some access flags have no corresponding
65 * modifier, such as {@linkplain #SYNTHETIC synthetic}.
66 *
67 * <p>The values for the constants representing the access and module
68 * flags are taken from sections of <cite>The Java Virtual Machine
69 * Specification</cite> including {@jvms 4.1} (class access and
145 STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS,
146 List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))),
147
148 /**
149 * The access flag {@code ACC_FINAL}, corresponding to the source
150 * modifier {@link Modifier#FINAL final}, with a mask
151 * value of <code>{@value "0x%04x" Modifier#FINAL}</code>.
152 */
153 FINAL(Modifier.FINAL, true,
154 Location.SET_FINAL_8,
155 List.of(Map.entry(RELEASE_7, Location.SET_CLASS_FIELD_METHOD_INNER_CLASS),
156 Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))),
157
158 /**
159 * The access flag {@code ACC_SUPER} with a mask value of {@code
160 * 0x0020}.
161 *
162 * @apiNote
163 * In Java SE 8 and above, the JVM treats the {@code ACC_SUPER}
164 * flag as set in every class file (JVMS {@jvms 4.1}).
165 */
166 SUPER(0x0000_0020, false, Location.SET_CLASS, List.of()),
167
168 /**
169 * The module flag {@code ACC_OPEN} with a mask value of {@code
170 * 0x0020}.
171 * @see java.lang.module.ModuleDescriptor#isOpen
172 */
173 OPEN(0x0000_0020, false, Location.SET_MODULE,
174 List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))),
175
176 /**
177 * The module requires flag {@code ACC_TRANSITIVE} with a mask
178 * value of {@code 0x0020}.
179 * @see java.lang.module.ModuleDescriptor.Requires.Modifier#TRANSITIVE
180 */
181 TRANSITIVE(0x0000_0020, false, Location.SET_MODULE_REQUIRES,
182 List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))),
183
184 /**
185 * The access flag {@code ACC_SYNCHRONIZED}, corresponding to the
186 * source modifier {@link Modifier#SYNCHRONIZED synchronized}, with
247 * value of <code>{@value "0x%04x" Modifier#ABSTRACT}</code>.
248 */
249 ABSTRACT(Modifier.ABSTRACT, true,
250 Location.SET_CLASS_METHOD_INNER_CLASS,
251 List.of(Map.entry(RELEASE_0, Location.SET_CLASS_METHOD))),
252
253 /**
254 * The access flag {@code ACC_STRICT}, corresponding to the source
255 * modifier {@link Modifier#STRICT strictfp}, with a mask value of
256 * <code>{@value "0x%04x" Modifier#STRICT}</code>.
257 *
258 * @apiNote
259 * The {@code ACC_STRICT} access flag is defined for class file
260 * major versions 46 through 60, inclusive (JVMS {@jvms 4.6}),
261 * corresponding to Java SE 1.2 through 16.
262 */
263 STRICT(Modifier.STRICT, true, Location.EMPTY_SET,
264 List.of(Map.entry(RELEASE_16, Location.SET_METHOD),
265 Map.entry(RELEASE_1, Location.EMPTY_SET))),
266
267 /**
268 * The access flag {@code ACC_SYNTHETIC} with a mask value of
269 * <code>{@value "0x%04x" Modifier#SYNTHETIC}</code>.
270 * @see Class#isSynthetic()
271 * @see Executable#isSynthetic()
272 * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC
273 */
274 SYNTHETIC(Modifier.SYNTHETIC, false, Location.SET_SYNTHETIC_9,
275 List.of(Map.entry(RELEASE_8, Location.SET_SYNTHETIC_8),
276 Map.entry(RELEASE_7, Location.SET_SYNTHETIC_5),
277 Map.entry(RELEASE_4, Location.EMPTY_SET))),
278
279 /**
280 * The access flag {@code ACC_ANNOTATION} with a mask value of
281 * <code>{@value "0x%04x" Modifier#ANNOTATION}</code>.
282 * @see Class#isAnnotation()
283 */
284 ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS,
285 List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))),
286
336 public int mask() {
337 return mask;
338 }
339
340 /**
341 * {@return whether or not this flag has a directly corresponding
342 * modifier in the Java programming language}
343 */
344 public boolean sourceModifier() {
345 return sourceModifier;
346 }
347
348 /**
349 * {@return locations this flag can be applied to in the current class file
350 * format version}
351 * <p>
352 * This method returns an empty set if this flag is not defined in
353 * the current class file format version.
354 */
355 public Set<Location> locations() {
356 return locations;
357 }
358
359 /**
360 * {@return locations this flag can be applied to in the given class file
361 * format version}
362 * <p>
363 * This method returns an empty set if this flag is not defined in
364 * the given {@code cffv}.
365 *
366 * @param cffv the class file format version to use
367 * @throws NullPointerException if the parameter is {@code null}
368 */
369 public Set<Location> locations(ClassFileFormatVersion cffv) {
370 return Location.findInHistory(locations, historicalLocations, cffv);
371 }
372
373 /**
374 * {@return an unmodifiable set of access flags for the given mask value
375 * appropriate for the location in the current class file format version}
376 *
377 * @param mask bit mask of access flags
378 * @param location context to interpret mask value
379 * @throws IllegalArgumentException if the mask contains bit
380 * positions not defined for the location in the current class file format
381 * @throws NullPointerException if {@code location} is {@code null}
382 */
383 public static Set<AccessFlag> maskToAccessFlags(int mask, Location location) {
384 var definition = findDefinition(location); // null checks location
385 int unmatchedMask = mask & (~location.flagsMask());
386 if (unmatchedMask != 0) {
387 throw new IllegalArgumentException("Unmatched bit position 0x" +
388 Integer.toHexString(unmatchedMask) +
389 " for location " + location);
390 }
391 return new AccessFlagSet(definition, mask);
392 }
393
394 /**
395 * {@return an unmodifiable set of access flags for the given mask value
396 * appropriate for the location in the given class file format version}
397 *
398 * @param mask bit mask of access flags
399 * @param location context to interpret mask value
400 * @param cffv the class file format to interpret mask value
401 * @throws IllegalArgumentException if the mask contains bit
402 * positions not defined for the location in the given class file format
403 * @throws NullPointerException if {@code location} or {@code cffv} is {@code null}
404 * @since 25
405 */
406 public static Set<AccessFlag> maskToAccessFlags(int mask, Location location, ClassFileFormatVersion cffv) {
407 var definition = findDefinition(location); // null checks location
408 int unmatchedMask = mask & (~location.flagsMask(cffv)); // null checks cffv
409 if (unmatchedMask != 0) {
410 throw new IllegalArgumentException("Unmatched bit position 0x" +
411 Integer.toHexString(unmatchedMask) +
412 " for location " + location +
413 " for class file format " + cffv);
414 }
415 return new AccessFlagSet(definition, mask);
416 }
417
418 /**
419 * A location within a {@code class} file where flags can be applied.
420 * <p>
421 * Note that since these locations represent {@code class} file structures
422 * rather than language structures, many language structures, such
423 * as constructors and interfaces, are <em>not</em> present.
424 * @since 20
425 */
426 public enum Location {
427 /**
428 * Class location.
429 *
430 * @see Class#accessFlags()
431 * @see ClassModel#flags()
432 * @jvms 4.1 The {@code ClassFile} Structure
433 */
434 CLASS(ACC_PUBLIC | ACC_FINAL | ACC_SUPER |
435 ACC_INTERFACE | ACC_ABSTRACT |
477 Map.entry(RELEASE_4, // no bridge, varargs, synthetic
478 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
479 ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
480 ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT),
481 Map.entry(RELEASE_1, // no strict
482 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
483 ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
484 ACC_NATIVE | ACC_ABSTRACT))),
485
486 /**
487 * Inner class location.
488 *
489 * @see Class#accessFlags()
490 * @see InnerClassInfo#flags()
491 * @jvms 4.7.6 The {@code InnerClasses} Attribute
492 */
493 INNER_CLASS(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
494 ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT |
495 ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM,
496 List.of(Map.entry(RELEASE_4, // no synthetic, annotation, enum
497 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
498 ACC_STATIC | ACC_FINAL | ACC_INTERFACE |
499 ACC_ABSTRACT),
500 Map.entry(RELEASE_0, 0))), // did not exist
501
502 /**
503 * Method parameter location.
504 *
505 * @see Parameter#accessFlags()
506 * @see MethodParameterInfo#flags()
507 * @jvms 4.7.24 The {@code MethodParameters} Attribute
508 */
509 METHOD_PARAMETER(ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED,
510 List.of(Map.entry(RELEASE_7, 0))), // did not exist
511
512 /**
513 * Module location.
514 *
515 * @see ModuleDescriptor#accessFlags()
516 * @see ModuleAttribute#moduleFlags()
517 * @jvms 4.7.25 The {@code Module} Attribute
518 */
519 MODULE(ACC_OPEN | ACC_SYNTHETIC | ACC_MANDATED,
595 MODULE_EXPORTS, MODULE_OPENS);
596 private static final Set<Location> SET_MANDATED_9 =
597 Set.of(METHOD_PARAMETER, // From 8
598 // Starting in 9
599 MODULE, MODULE_REQUIRES,
600 MODULE_EXPORTS, MODULE_OPENS);
601
602 private final int flagsMask;
603 private final List<Map.Entry<ClassFileFormatVersion, Integer>> historicalFlagsMasks;
604
605 Location(int flagsMask,
606 List<Map.Entry<ClassFileFormatVersion, Integer>> historicalFlagsMasks) {
607 this.flagsMask = flagsMask;
608 this.historicalFlagsMasks = ensureHistoryOrdered(historicalFlagsMasks);
609 }
610
611 // Ensures the historical versions are from newest to oldest and do not include the latest
612 // These 2 utilities reside in Location because Location must be initialized before AccessFlag
613 private static <T> List<Map.Entry<ClassFileFormatVersion, T>> ensureHistoryOrdered(
614 List<Map.Entry<ClassFileFormatVersion, T>> history) {
615 ClassFileFormatVersion lastVersion = ClassFileFormatVersion.latest();
616 for (var e : history) {
617 var historyVersion = e.getKey();
618 if (lastVersion.compareTo(historyVersion) <= 0) {
619 throw new IllegalArgumentException("Versions out of order");
620 }
621 lastVersion = historyVersion;
622 }
623 return history;
624 }
625
626 private static <T> T findInHistory(T candidate, List<Map.Entry<ClassFileFormatVersion, T>> history,
627 ClassFileFormatVersion cffv) {
628 Objects.requireNonNull(cffv);
629 for (var e : history) {
630 if (e.getKey().compareTo(cffv) < 0) {
631 // last version found was valid
632 return candidate;
633 }
634 candidate = e.getValue();
635 }
636 return candidate;
637 }
638
639 /**
640 * {@return the union of masks of all access flags defined for
641 * this location in the current class file format version}
642 * <p>
643 * This method returns {@code 0} if this location does not exist in
644 * the current class file format version.
645 *
646 * @since 25
647 */
648 public int flagsMask() {
649 return flagsMask;
650 }
651
652 /**
653 * {@return the union of masks of all access flags defined for
654 * this location in the given class file format version}
655 * <p>
656 * This method returns {@code 0} if this location does not exist in
657 * the given {@code cffv}.
658 *
659 * @param cffv the class file format version
660 * @throws NullPointerException if {@code cffv} is {@code null}
661 * @since 25
662 */
663 public int flagsMask(ClassFileFormatVersion cffv) {
664 return findInHistory(flagsMask, historicalFlagsMasks, cffv);
665 }
666
667 /**
668 * {@return the set of access flags defined for this location in the
669 * current class file format version} The set is immutable.
670 * <p>
671 * This method returns an empty set if this location does not exist
672 * in the current class file format version.
673 *
674 * @since 25
675 */
676 public Set<AccessFlag> flags() {
677 return new AccessFlagSet(findDefinition(this), flagsMask());
678 }
679
680 /**
681 * {@return the set of access flags defined for this location in the
682 * given class file format version} The set is immutable.
683 * <p>
684 * This method returns an empty set if this location does not exist
685 * in the given {@code cffv}.
686 *
687 * @param cffv the class file format version
688 * @throws NullPointerException if {@code cffv} is {@code null}
689 * @since 25
690 */
691 public Set<AccessFlag> flags(ClassFileFormatVersion cffv) {
692 // implicit null check cffv
693 return new AccessFlagSet(findDefinition(this), flagsMask(cffv));
694 }
695 }
696
697 private static AccessFlag[] createDefinition(AccessFlag... known) {
698 var ret = new AccessFlag[Character.SIZE];
699 for (var flag : known) {
700 var mask = flag.mask;
701 int pos = Integer.numberOfTrailingZeros(mask);
702 assert ret[pos] == null : ret[pos] + " " + flag;
703 ret[pos] = flag;
704 }
705 return ret;
706 }
707
708 // Will take extra args in the future for valhalla switch
709 private static AccessFlag[] findDefinition(Location location) {
710 return switch (location) {
711 case CLASS -> CLASS_FLAGS;
712 case FIELD -> FIELD_FLAGS;
713 case METHOD -> METHOD_FLAGS;
714 case INNER_CLASS -> INNER_CLASS_FLAGS;
715 case METHOD_PARAMETER -> METHOD_PARAMETER_FLAGS;
716 case MODULE -> MODULE_FLAGS;
717 case MODULE_REQUIRES -> MODULE_REQUIRES_FLAGS;
718 case MODULE_EXPORTS -> MODULE_EXPORTS_FLAGS;
719 case MODULE_OPENS -> MODULE_OPENS_FLAGS;
720 };
721 }
722
723 private static final @Stable AccessFlag[] // Can use stable array and lazy init in the future
724 CLASS_FLAGS = createDefinition(PUBLIC, FINAL, SUPER, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM, MODULE),
725 FIELD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, VOLATILE, TRANSIENT, SYNTHETIC, ENUM),
726 METHOD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, BRIDGE, VARARGS, NATIVE, ABSTRACT, STRICT, SYNTHETIC),
727 INNER_CLASS_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM),
728 METHOD_PARAMETER_FLAGS = createDefinition(FINAL, SYNTHETIC, MANDATED),
729 MODULE_FLAGS = createDefinition(OPEN, SYNTHETIC, MANDATED),
730 MODULE_REQUIRES_FLAGS = createDefinition(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED),
731 MODULE_EXPORTS_FLAGS = createDefinition(SYNTHETIC, MANDATED),
732 MODULE_OPENS_FLAGS = createDefinition(SYNTHETIC, MANDATED);
733
734 private static int undefinedMask(AccessFlag[] definition, int mask) {
735 assert definition.length == Character.SIZE;
736 int definedMask = 0;
737 for (int i = 0; i < Character.SIZE; i++) {
738 if (definition[i] != null) {
739 definedMask |= 1 << i;
740 }
741 }
742 return mask & ~definedMask;
743 }
744
745 private static final class AccessFlagSet extends AbstractSet<AccessFlag> {
746 private final @Stable AccessFlag[] definition;
747 private final int mask;
748
749 // all mutating methods throw UnsupportedOperationException
750 @Override public boolean add(AccessFlag e) { throw uoe(); }
751 @Override public boolean addAll(Collection<? extends AccessFlag> c) { throw uoe(); }
752 @Override public void clear() { throw uoe(); }
753 @Override public boolean remove(Object o) { throw uoe(); }
754 @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
755 @Override public boolean removeIf(Predicate<? super AccessFlag> filter) { throw uoe(); }
756 @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
757 private static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
758
759 private AccessFlagSet(AccessFlag[] definition, int mask) {
760 assert undefinedMask(definition, mask) == 0 : mask;
761 this.definition = definition;
762 this.mask = mask;
763 }
764
765 @Override
766 public Iterator<AccessFlag> iterator() {
767 return new AccessFlagIterator(definition, mask);
768 }
769
770 @Override
771 public void forEach(Consumer<? super AccessFlag> action) {
772 Objects.requireNonNull(action); // in case of empty
773 for (int i = 0; i < Character.SIZE; i++) {
774 if ((mask & (1 << i)) != 0) {
775 action.accept(definition[i]);
776 }
777 }
778 }
779
780 private static final class AccessFlagIterator implements Iterator<AccessFlag> {
781 private final @Stable AccessFlag[] definition;
782 private int remainingMask;
783
784 private AccessFlagIterator(AccessFlag[] definition, int remainingMask) {
785 this.definition = definition;
786 this.remainingMask = remainingMask;
787 }
788
789 @Override
790 public boolean hasNext() {
791 return remainingMask != 0;
792 }
793
794 @Override
795 public AccessFlag next() {
796 int flagBit = Integer.lowestOneBit(remainingMask);
797 if (flagBit == 0) {
798 throw new NoSuchElementException();
799 }
800 remainingMask &= ~flagBit;
801 return definition[Integer.numberOfTrailingZeros(flagBit)];
802 }
803 }
804
805 @Override
806 public int size() {
807 return Integer.bitCount(mask);
808 }
809
810 @Override
811 public boolean contains(Object o) {
812 if (Objects.requireNonNull(o) instanceof AccessFlag flag) {
813 int bit = flag.mask;
814 return (bit & mask) != 0 && definition[Integer.numberOfTrailingZeros(bit)] == flag;
815 }
816 return false;
817 }
818
819 @Override
820 public boolean isEmpty() {
821 return mask == 0;
822 }
823 }
824 }
|
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 java.lang.reflect;
27
28 import jdk.internal.javac.PreviewFeature;
29 import jdk.internal.reflect.AccessFlagSet;
30
31 import java.lang.classfile.ClassFile;
32 import java.lang.classfile.ClassModel;
33 import java.lang.classfile.FieldModel;
34 import java.lang.classfile.MethodModel;
35 import java.lang.classfile.attribute.InnerClassInfo;
36 import java.lang.classfile.attribute.MethodParameterInfo;
37 import java.lang.classfile.attribute.ModuleAttribute;
38 import java.lang.classfile.attribute.ModuleExportInfo;
39 import java.lang.classfile.attribute.ModuleOpenInfo;
40 import java.lang.classfile.attribute.ModuleRequireInfo;
41 import java.lang.module.ModuleDescriptor;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.Set;
46
47 import static java.lang.classfile.ClassFile.*;
48 import static java.lang.reflect.ClassFileFormatVersion.*;
49
50 /**
51 * Represents a JVM access or module-related flag on a runtime member,
52 * such as a {@linkplain Class class}, {@linkplain Field field}, or
53 * {@linkplain Executable method}.
54 *
55 * <P>JVM access and module-related flags are related to, but distinct
56 * from Java language {@linkplain Modifier modifiers}. Some modifiers
57 * and access flags have a one-to-one correspondence, such as {@code
58 * public}. In other cases, some language-level modifiers do
59 * <em>not</em> have an access flag, such as {@code sealed} (JVMS
60 * {@jvms 4.7.31}) and some access flags have no corresponding
61 * modifier, such as {@linkplain #SYNTHETIC synthetic}.
62 *
63 * <p>The values for the constants representing the access and module
64 * flags are taken from sections of <cite>The Java Virtual Machine
65 * Specification</cite> including {@jvms 4.1} (class access and
141 STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS,
142 List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))),
143
144 /**
145 * The access flag {@code ACC_FINAL}, corresponding to the source
146 * modifier {@link Modifier#FINAL final}, with a mask
147 * value of <code>{@value "0x%04x" Modifier#FINAL}</code>.
148 */
149 FINAL(Modifier.FINAL, true,
150 Location.SET_FINAL_8,
151 List.of(Map.entry(RELEASE_7, Location.SET_CLASS_FIELD_METHOD_INNER_CLASS),
152 Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))),
153
154 /**
155 * The access flag {@code ACC_SUPER} with a mask value of {@code
156 * 0x0020}.
157 *
158 * @apiNote
159 * In Java SE 8 and above, the JVM treats the {@code ACC_SUPER}
160 * flag as set in every class file (JVMS {@jvms 4.1}).
161 * If preview feature is enabled,
162 * the {@code 0x0020} access flag bit is {@linkplain #IDENTITY IDENTITY access flag}.
163 */
164 SUPER(0x0000_0020, false,
165 Location.SET_CLASS,
166 List.of()),
167
168 /**
169 * The access flag {@code ACC_IDENTITY} with a mask value of
170 * <code>{@value "0x%04x" ClassFile#ACC_IDENTITY}</code>.
171 *
172 * @jvms value-objects-4.1 Class access and property modifiers
173 * @since Valhalla
174 */
175 @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
176 IDENTITY(ACC_IDENTITY, false,
177 Location.EMPTY_SET,
178 List.of()),
179
180 /**
181 * The module flag {@code ACC_OPEN} with a mask value of {@code
182 * 0x0020}.
183 * @see java.lang.module.ModuleDescriptor#isOpen
184 */
185 OPEN(0x0000_0020, false, Location.SET_MODULE,
186 List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))),
187
188 /**
189 * The module requires flag {@code ACC_TRANSITIVE} with a mask
190 * value of {@code 0x0020}.
191 * @see java.lang.module.ModuleDescriptor.Requires.Modifier#TRANSITIVE
192 */
193 TRANSITIVE(0x0000_0020, false, Location.SET_MODULE_REQUIRES,
194 List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))),
195
196 /**
197 * The access flag {@code ACC_SYNCHRONIZED}, corresponding to the
198 * source modifier {@link Modifier#SYNCHRONIZED synchronized}, with
259 * value of <code>{@value "0x%04x" Modifier#ABSTRACT}</code>.
260 */
261 ABSTRACT(Modifier.ABSTRACT, true,
262 Location.SET_CLASS_METHOD_INNER_CLASS,
263 List.of(Map.entry(RELEASE_0, Location.SET_CLASS_METHOD))),
264
265 /**
266 * The access flag {@code ACC_STRICT}, corresponding to the source
267 * modifier {@link Modifier#STRICT strictfp}, with a mask value of
268 * <code>{@value "0x%04x" Modifier#STRICT}</code>.
269 *
270 * @apiNote
271 * The {@code ACC_STRICT} access flag is defined for class file
272 * major versions 46 through 60, inclusive (JVMS {@jvms 4.6}),
273 * corresponding to Java SE 1.2 through 16.
274 */
275 STRICT(Modifier.STRICT, true, Location.EMPTY_SET,
276 List.of(Map.entry(RELEASE_16, Location.SET_METHOD),
277 Map.entry(RELEASE_1, Location.EMPTY_SET))),
278
279 /**
280 * The access flag {@code ACC_STRICT_INIT}, with a mask value of
281 * <code>{@value "0x%04x" ClassFile#ACC_STRICT_INIT}</code>.
282 *
283 * @jvms strict-fields-4.5 Field access and property flags
284 * @since Valhalla
285 */
286 @PreviewFeature(feature = PreviewFeature.Feature.STRICT_FIELDS, reflective=true)
287 STRICT_INIT(ACC_STRICT_INIT, false,
288 Location.EMPTY_SET,
289 List.of()),
290
291 /**
292 * The access flag {@code ACC_SYNTHETIC} with a mask value of
293 * <code>{@value "0x%04x" Modifier#SYNTHETIC}</code>.
294 * @see Class#isSynthetic()
295 * @see Executable#isSynthetic()
296 * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC
297 */
298 SYNTHETIC(Modifier.SYNTHETIC, false, Location.SET_SYNTHETIC_9,
299 List.of(Map.entry(RELEASE_8, Location.SET_SYNTHETIC_8),
300 Map.entry(RELEASE_7, Location.SET_SYNTHETIC_5),
301 Map.entry(RELEASE_4, Location.EMPTY_SET))),
302
303 /**
304 * The access flag {@code ACC_ANNOTATION} with a mask value of
305 * <code>{@value "0x%04x" Modifier#ANNOTATION}</code>.
306 * @see Class#isAnnotation()
307 */
308 ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS,
309 List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))),
310
360 public int mask() {
361 return mask;
362 }
363
364 /**
365 * {@return whether or not this flag has a directly corresponding
366 * modifier in the Java programming language}
367 */
368 public boolean sourceModifier() {
369 return sourceModifier;
370 }
371
372 /**
373 * {@return locations this flag can be applied to in the current class file
374 * format version}
375 * <p>
376 * This method returns an empty set if this flag is not defined in
377 * the current class file format version.
378 */
379 public Set<Location> locations() {
380 return locations(latest());
381 }
382
383 /**
384 * {@return locations this flag can be applied to in the given class file
385 * format version}
386 * <p>
387 * This method returns an empty set if this flag is not defined in
388 * the given {@code cffv}.
389 *
390 * @param cffv the class file format version to use
391 * @throws NullPointerException if the parameter is {@code null}
392 */
393 public Set<Location> locations(ClassFileFormatVersion cffv) {
394 return Location.findInHistory(locations, historicalLocations, cffv);
395 }
396
397 /**
398 * {@return an unmodifiable set of access flags for the given mask value
399 * appropriate for the location in the current class file format version}
400 *
401 * @param mask bit mask of access flags
402 * @param location context to interpret mask value
403 * @throws IllegalArgumentException if the mask contains bit
404 * positions not defined for the location in the current class file format
405 * @throws NullPointerException if {@code location} is {@code null}
406 */
407 public static Set<AccessFlag> maskToAccessFlags(int mask, Location location) {
408 return maskToAccessFlags(mask, location, latest());
409 }
410
411 /**
412 * {@return an unmodifiable set of access flags for the given mask value
413 * appropriate for the location in the given class file format version}
414 *
415 * @param mask bit mask of access flags
416 * @param location context to interpret mask value
417 * @param cffv the class file format to interpret mask value
418 * @throws IllegalArgumentException if the mask contains bit
419 * positions not defined for the location in the given class file format
420 * @throws NullPointerException if {@code location} or {@code cffv} is {@code null}
421 * @since 25
422 */
423 public static Set<AccessFlag> maskToAccessFlags(int mask, Location location, ClassFileFormatVersion cffv) {
424 var definition = AccessFlagSet.findDefinition(location, cffv); // null checks location
425 int unmatchedMask = mask & (~location.flagsMask(cffv));
426 if (unmatchedMask != 0) {
427 throw new IllegalArgumentException("Unmatched bit position 0x" +
428 Integer.toHexString(unmatchedMask) +
429 " for location " + location +
430 " for class file format " + cffv);
431 }
432 return AccessFlagSet.ofValidated(definition, mask);
433 }
434
435 /**
436 * A location within a {@code class} file where flags can be applied.
437 * <p>
438 * Note that since these locations represent {@code class} file structures
439 * rather than language structures, many language structures, such
440 * as constructors and interfaces, are <em>not</em> present.
441 * @since 20
442 */
443 public enum Location {
444 /**
445 * Class location.
446 *
447 * @see Class#accessFlags()
448 * @see ClassModel#flags()
449 * @jvms 4.1 The {@code ClassFile} Structure
450 */
451 CLASS(ACC_PUBLIC | ACC_FINAL | ACC_SUPER |
452 ACC_INTERFACE | ACC_ABSTRACT |
494 Map.entry(RELEASE_4, // no bridge, varargs, synthetic
495 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
496 ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
497 ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT),
498 Map.entry(RELEASE_1, // no strict
499 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
500 ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
501 ACC_NATIVE | ACC_ABSTRACT))),
502
503 /**
504 * Inner class location.
505 *
506 * @see Class#accessFlags()
507 * @see InnerClassInfo#flags()
508 * @jvms 4.7.6 The {@code InnerClasses} Attribute
509 */
510 INNER_CLASS(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
511 ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT |
512 ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM,
513 List.of(Map.entry(RELEASE_4, // no synthetic, annotation, enum
514 ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED |
515 ACC_STATIC | ACC_FINAL | ACC_INTERFACE |
516 ACC_ABSTRACT),
517 Map.entry(RELEASE_0, 0))), // did not exist
518
519 /**
520 * Method parameter location.
521 *
522 * @see Parameter#accessFlags()
523 * @see MethodParameterInfo#flags()
524 * @jvms 4.7.24 The {@code MethodParameters} Attribute
525 */
526 METHOD_PARAMETER(ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED,
527 List.of(Map.entry(RELEASE_7, 0))), // did not exist
528
529 /**
530 * Module location.
531 *
532 * @see ModuleDescriptor#accessFlags()
533 * @see ModuleAttribute#moduleFlags()
534 * @jvms 4.7.25 The {@code Module} Attribute
535 */
536 MODULE(ACC_OPEN | ACC_SYNTHETIC | ACC_MANDATED,
612 MODULE_EXPORTS, MODULE_OPENS);
613 private static final Set<Location> SET_MANDATED_9 =
614 Set.of(METHOD_PARAMETER, // From 8
615 // Starting in 9
616 MODULE, MODULE_REQUIRES,
617 MODULE_EXPORTS, MODULE_OPENS);
618
619 private final int flagsMask;
620 private final List<Map.Entry<ClassFileFormatVersion, Integer>> historicalFlagsMasks;
621
622 Location(int flagsMask,
623 List<Map.Entry<ClassFileFormatVersion, Integer>> historicalFlagsMasks) {
624 this.flagsMask = flagsMask;
625 this.historicalFlagsMasks = ensureHistoryOrdered(historicalFlagsMasks);
626 }
627
628 // Ensures the historical versions are from newest to oldest and do not include the latest
629 // These 2 utilities reside in Location because Location must be initialized before AccessFlag
630 private static <T> List<Map.Entry<ClassFileFormatVersion, T>> ensureHistoryOrdered(
631 List<Map.Entry<ClassFileFormatVersion, T>> history) {
632 ClassFileFormatVersion lastVersion = latest();
633 for (var e : history) {
634 var historyVersion = e.getKey();
635 if (lastVersion.compareTo(historyVersion) <= 0) {
636 throw new IllegalArgumentException("Versions out of order");
637 }
638 lastVersion = historyVersion;
639 }
640 return history;
641 }
642
643 private static <T> T findInHistory(T candidate, List<Map.Entry<ClassFileFormatVersion, T>> history,
644 ClassFileFormatVersion cffv) {
645 Objects.requireNonNull(cffv);
646 for (var e : history) {
647 if (e.getKey().compareTo(cffv) < 0) {
648 // last version found was valid
649 return candidate;
650 }
651 candidate = e.getValue();
652 }
653 return candidate;
654 }
655
656 /**
657 * {@return the union of masks of all access flags defined for
658 * this location in the current class file format version}
659 * <p>
660 * This method returns {@code 0} if this location does not exist in
661 * the current class file format version.
662 *
663 * @since 25
664 */
665 public int flagsMask() {
666 return flagsMask(latest());
667 }
668
669 /**
670 * {@return the union of masks of all access flags defined for
671 * this location in the given class file format version}
672 * <p>
673 * This method returns {@code 0} if this location does not exist in
674 * the given {@code cffv}.
675 *
676 * @param cffv the class file format version
677 * @throws NullPointerException if {@code cffv} is {@code null}
678 * @since 25
679 */
680 public int flagsMask(ClassFileFormatVersion cffv) {
681 return findInHistory(flagsMask, historicalFlagsMasks, cffv);
682 }
683
684 /**
685 * {@return the set of access flags defined for this location in the
686 * current class file format version} The set is immutable.
687 * <p>
688 * This method returns an empty set if this location does not exist
689 * in the current class file format version.
690 *
691 * @since 25
692 */
693 public Set<AccessFlag> flags() {
694 return flags(latest());
695 }
696
697 /**
698 * {@return the set of access flags defined for this location in the
699 * given class file format version} The set is immutable.
700 * <p>
701 * This method returns an empty set if this location does not exist
702 * in the given {@code cffv}.
703 *
704 * @param cffv the class file format version
705 * @throws NullPointerException if {@code cffv} is {@code null}
706 * @since 25
707 */
708 public Set<AccessFlag> flags(ClassFileFormatVersion cffv) {
709 // flagsMask null checks cffv and always returns valid mask
710 return AccessFlagSet.ofValidated(AccessFlagSet.findDefinition(this, cffv), flagsMask(cffv));
711 }
712 }
713 }
|