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}