/*
* Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.jvm.java.abi;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import org.objectweb.asm.Opcodes;
/** Computes the access flags (see JVMS8 4.1, 4.5, 4.6) for {@link Element}s. */
public final class AccessFlags {
private AccessFlags() {}
/**
* Gets the class access flags (see JVMS8 4.1) for the given type element, augmented by the
* special ASM pseudo-access flag for @Deprecated types.
*/
public static int getAccessFlags(TypeElement typeElement) {
int result = getCommonAccessFlags(typeElement);
switch (typeElement.getKind()) {
case ANNOTATION_TYPE:
// No ACC_SUPER here per JVMS 4.1
result = result | Opcodes.ACC_ANNOTATION;
result = result | Opcodes.ACC_INTERFACE;
result = result | Opcodes.ACC_ABSTRACT;
break;
case ENUM:
result = result | Opcodes.ACC_SUPER; // JVMS 4.1
result = result | Opcodes.ACC_ENUM;
// Enums have this lovely property that you can't declare them abstract in source, even
// if they have abstract methods or incompletely implemented interfaces, yet the class
// file will have ACC_ABSTRACT in that case. Because it's a pain to figure out if an
// enum is abstract (and impossible in the no-deps case), and you can't instantiate or
// subclass one directly anyway, we just leave the flag off.
break;
case INTERFACE:
// No ACC_SUPER here per JVMS 4.1
result = result | Opcodes.ACC_ABSTRACT;
result = result | Opcodes.ACC_INTERFACE;
break;
// $CASES-OMITTED$
default:
result = result | Opcodes.ACC_SUPER; // JVMS 4.1
break;
}
return result;
}
/**
* Gets the method access flags (see JVMS8 4.6) for the given executable element, augmented by the
* special ASM pseudo-access flag for @Deprecated methods.
*/
public static int getAccessFlags(ExecutableElement executableElement) {
int result = getCommonAccessFlags(executableElement);
if (executableElement.isVarArgs()) {
result = result | Opcodes.ACC_VARARGS;
}
return result;
}
/**
* Gets the field access flags (see JVMS8 4.5) for the given variable element, augmented by the
* special ASM pseudo-access flag for @Deprecated fields.
*/
public static int getAccessFlags(VariableElement variableElement) {
int result = getCommonAccessFlags(variableElement);
if (variableElement.getKind() == ElementKind.ENUM_CONSTANT) {
result = result | Opcodes.ACC_ENUM;
}
return result;
}
/**
* Gets the access flags (see JVMS8 4.1, 4.5, 4.6) for the given element, from among those that
* are common to all kinds of elements.
*/
private static int getCommonAccessFlags(Element element) {
int result = modifiersToAccessFlags(element.getModifiers());
if (isDeprecated(element)) {
result = result | Opcodes.ACC_DEPRECATED;
}
return result;
}
private static boolean isDeprecated(Element element) {
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
DeclaredType annotationType = annotationMirror.getAnnotationType();
TypeElement annotationTypeElement = (TypeElement) annotationType.asElement();
// Note: We can't use Types.isSameType against a type obtained from
// Elements.getTypeElement().asType() here because it appears that getTypeElement actually
// returns a different element for some fundamental classes than is actually used for
// compilation.
if (annotationTypeElement.getQualifiedName().contentEquals("java.lang.Deprecated")) {
return true;
}
}
return false;
}
/**
* Gets the access flags (see JVMS8 4.1, 4.5, 4.6) corresponding to the given set of modifiers.
*/
private static int modifiersToAccessFlags(Set<Modifier> modifiers) {
int result = 0;
for (Modifier modifier : modifiers) {
result = result | modifierToAccessFlag(modifier);
}
return result;
}
/** Gets the access flag (see JVMS8 4.1, 4.5, 4.6) corresponding to the given modifier. */
private static int modifierToAccessFlag(Modifier modifier) {
switch (modifier) {
case PUBLIC:
return Opcodes.ACC_PUBLIC;
case PROTECTED:
return Opcodes.ACC_PROTECTED;
case PRIVATE:
return Opcodes.ACC_PRIVATE;
case ABSTRACT:
return Opcodes.ACC_ABSTRACT;
case DEFAULT:
return 0;
case STATIC:
return Opcodes.ACC_STATIC;
case FINAL:
return Opcodes.ACC_FINAL;
case TRANSIENT:
return Opcodes.ACC_TRANSIENT;
case VOLATILE:
return Opcodes.ACC_VOLATILE;
case SYNCHRONIZED:
return Opcodes.ACC_SYNCHRONIZED;
case NATIVE:
return Opcodes.ACC_NATIVE;
case STRICTFP:
return Opcodes.ACC_STRICT;
default:
throw new IllegalArgumentException(String.format("Unexpected modifier: %s", modifier));
}
}
}