001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2025 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.construct.vm; 015 016// Even though this is in the java.lang.reflect package no reflection takes place; it's just an enum 017import java.lang.reflect.AccessFlag; 018 019import javax.lang.model.element.Element; 020import javax.lang.model.element.ElementKind; 021import javax.lang.model.element.ExecutableElement; 022import javax.lang.model.element.Modifier; 023import javax.lang.model.element.ModuleElement; 024 025import javax.lang.model.util.Elements.Origin; 026 027import org.microbean.construct.Domain; 028 029/** 030 * A utility class for working with access flags. 031 * 032 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 033 * 034 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1 Java Virtual Machine 035 * Specification, section 4.5 036 * 037 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.6-200-A.1 Java Virtual Machine 038 * Specification, section 4.6 039 * 040 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.7 Java Virtual Machine Specification, 041 * section 4.7 042 */ 043public final class AccessFlags { 044 045 // Flags pulled from the ASM library (https://gitlab.ow2.org/asm/asm/-/blob/master/asm/src/main/java/org/objectweb/asm/Opcodes.java): 046 047 private static final int ASM_RECORD = 1 << 16; // 0x10000 // class // (see for example https://github.com/raphw/byte-buddy/blob/byte-buddy-1.14.5/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java#L2949) 048 049 private AccessFlags() { 050 super(); 051 } 052 053 /** 054 * Returns the <dfn>access flags</dfn> for the supplied {@link Element}, which is presumed to have come from the 055 * supplied {@link Domain}. 056 * 057 * @param e an {@link Element}; must not be {@code null} 058 * 059 * @param domain a {@link Domain}; must not be {@code null} 060 * 061 * @return the access flags for the supplied {@link Element} 062 * 063 * @exception NullPointerException if either argument is {@code null} 064 * 065 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1 Java Virtual Machine 066 * Specification, section 4.5 067 * 068 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.6-200-A.1 Java Virtual Machine 069 * Specification, section 4.6 070 * 071 * @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.7 Java Virtual Machine Specification, 072 * section 4.7 073 */ 074 @SuppressWarnings("try") 075 public static final int accessFlags(final Element e, final Domain domain) { 076 int accessFlags = 0; 077 ElementKind k; 078 try (var lock = domain.lock()) { 079 for (final Modifier m : e.getModifiers()) { // not sure this actually causes symbol completion; could be hoisted out of lock 080 accessFlags |= accessFlagMask(m); 081 } 082 k = e.getKind(); 083 switch (k) { 084 case METHOD: 085 // Handle just bridge and varargs here; other stuff will happen later 086 final ExecutableElement ee = (ExecutableElement)e; 087 if (domain.bridge(ee)) { 088 accessFlags |= AccessFlag.BRIDGE.mask(); 089 } 090 if (ee.isVarArgs()) { 091 accessFlags |= AccessFlag.VARARGS.mask(); 092 } 093 break; 094 case MODULE: 095 // Handle just openness here; other stuff will happen later 096 if (((ModuleElement)e).isOpen()) { 097 accessFlags |= AccessFlag.OPEN.mask(); 098 } 099 break; 100 } 101 accessFlags |= accessFlagMask(domain.origin(e)); 102 } 103 accessFlags |= accessFlagMask(k); 104 return accessFlags; 105 } 106 107 private static final int accessFlagMask(final Modifier m) { 108 return switch (m) { 109 case null -> throw new NullPointerException("m"); 110 case ABSTRACT -> AccessFlag.ABSTRACT.mask(); 111 case FINAL -> AccessFlag.FINAL.mask(); 112 case NATIVE -> AccessFlag.NATIVE.mask(); 113 case PRIVATE -> AccessFlag.PRIVATE.mask(); 114 case PROTECTED -> AccessFlag.PROTECTED.mask(); 115 case PUBLIC -> AccessFlag.PUBLIC.mask(); 116 case STATIC -> AccessFlag.STATIC.mask(); 117 case STRICTFP -> AccessFlag.STRICT.mask(); 118 case SYNCHRONIZED -> AccessFlag.SYNCHRONIZED.mask(); 119 case TRANSIENT -> AccessFlag.TRANSIENT.mask(); 120 case VOLATILE -> AccessFlag.VOLATILE.mask(); 121 case DEFAULT, NON_SEALED, SEALED -> 0; // pass through 122 }; 123 } 124 125 private static final int accessFlagMask(final ElementKind k) { 126 return switch (k) { 127 case null -> throw new NullPointerException("k"); 128 case ANNOTATION_TYPE -> AccessFlag.ANNOTATION.mask() | AccessFlag.INTERFACE.mask(); // see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.1-200-E.1 129 case ENUM -> AccessFlag.ENUM.mask(); 130 case ENUM_CONSTANT -> AccessFlag.ENUM.mask(); // perhaps odd but see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1 131 case INTERFACE -> AccessFlag.INTERFACE.mask(); // AccessFlag.ABSTRACT.mask() needs to be in play too, but that's covered in accessFlagMask(Modifier) 132 case MODULE -> AccessFlag.MODULE.mask(); 133 case RECORD -> ASM_RECORD; // Some bytecode libraries use this to divine "recordness"; AccessFlag doesn't expose it and the JVM spec doesn't define it 134 case 135 BINDING_VARIABLE, 136 CLASS, 137 CONSTRUCTOR, 138 EXCEPTION_PARAMETER, 139 FIELD, 140 INSTANCE_INIT, 141 LOCAL_VARIABLE, 142 METHOD, 143 OTHER, 144 PACKAGE, 145 PARAMETER, 146 RECORD_COMPONENT, 147 RESOURCE_VARIABLE, 148 STATIC_INIT, 149 TYPE_PARAMETER -> 0; // pass through 150 }; 151 } 152 153 private static final int accessFlagMask(final Origin o) { 154 return switch (o) { 155 case null -> throw new NullPointerException("o"); 156 case EXPLICIT -> 0; 157 case MANDATED -> AccessFlag.MANDATED.mask(); 158 case SYNTHETIC -> AccessFlag.SYNTHETIC.mask(); 159 }; 160 } 161 162}