com.google.j2cl.transpiler.frontend.javac.JavaEnvironment Maven / Gradle / Ivy
/*
* Copyright 2015 Google 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.google.j2cl.transpiler.frontend.javac;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.j2cl.transpiler.frontend.common.FrontendConstants.HAS_NO_SIDE_EFFECTS_ANNOTATION_NAME;
import static com.google.j2cl.transpiler.frontend.common.FrontendConstants.UNCHECKED_CAST_ANNOTATION_NAME;
import static com.google.j2cl.transpiler.frontend.common.FrontendConstants.WASM_ANNOTATION_NAME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.j2cl.common.InternalCompilerError;
import com.google.j2cl.common.SourcePosition;
import com.google.j2cl.transpiler.ast.ArrayTypeDescriptor;
import com.google.j2cl.transpiler.ast.BinaryOperator;
import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor;
import com.google.j2cl.transpiler.ast.FieldDescriptor;
import com.google.j2cl.transpiler.ast.IntersectionTypeDescriptor;
import com.google.j2cl.transpiler.ast.JsEnumInfo;
import com.google.j2cl.transpiler.ast.JsInfo;
import com.google.j2cl.transpiler.ast.Literal;
import com.google.j2cl.transpiler.ast.MethodDescriptor;
import com.google.j2cl.transpiler.ast.MethodDescriptor.ParameterDescriptor;
import com.google.j2cl.transpiler.ast.PackageDeclaration;
import com.google.j2cl.transpiler.ast.PostfixOperator;
import com.google.j2cl.transpiler.ast.PrefixOperator;
import com.google.j2cl.transpiler.ast.PrimitiveTypes;
import com.google.j2cl.transpiler.ast.TypeDeclaration;
import com.google.j2cl.transpiler.ast.TypeDeclaration.DescriptorFactory;
import com.google.j2cl.transpiler.ast.TypeDeclaration.Kind;
import com.google.j2cl.transpiler.ast.TypeDescriptor;
import com.google.j2cl.transpiler.ast.TypeDescriptors;
import com.google.j2cl.transpiler.ast.TypeVariable;
import com.google.j2cl.transpiler.ast.UnionTypeDescriptor;
import com.google.j2cl.transpiler.ast.Variable;
import com.google.j2cl.transpiler.ast.Visibility;
import com.google.j2cl.transpiler.frontend.common.Nullability;
import com.google.j2cl.transpiler.frontend.common.PackageInfoCache;
import com.sun.tools.javac.code.Attribute.TypeCompound;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.CapturedType;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.IntersectionClassType;
import com.sun.tools.javac.code.Type.JCPrimitiveType;
import com.sun.tools.javac.code.Type.UnionClassType;
import com.sun.tools.javac.code.TypeAnnotationPosition;
import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.util.Context;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.lang.model.AnnotatedConstruct;
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.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.Parameterizable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
/** Utility functions to interact with JavaC internal representations. */
public class JavaEnvironment {
JavacTypes javacTypes;
Types internalTypes;
JavacElements elements;
public JavaEnvironment(Context context, Collection wellKnownQualifiedBinaryNames) {
this.javacTypes = JavacTypes.instance(context);
this.internalTypes = Types.instance(context);
this.elements = JavacElements.instance(context);
initWellKnownTypes(wellKnownQualifiedBinaryNames);
}
private void initWellKnownTypes(Collection wellKnownQualifiedBinaryNames) {
checkState(!TypeDescriptors.isInitialized());
TypeDescriptors.SingletonBuilder builder = new TypeDescriptors.SingletonBuilder();
// Add well-known, non-primitive types.
wellKnownQualifiedBinaryNames.forEach(
binaryName -> {
String qualifiedSourceName = binaryName.replace('$', '.');
TypeElement element = getTypeElement(qualifiedSourceName);
if (element != null) {
builder.addReferenceType(createDeclaredTypeDescriptor(element.asType()));
}
});
builder.buildSingleton();
}
@Nullable
static PrefixOperator getPrefixOperator(com.sun.source.tree.Tree.Kind operator) {
switch (operator) {
case PREFIX_INCREMENT:
return PrefixOperator.INCREMENT;
case PREFIX_DECREMENT:
return PrefixOperator.DECREMENT;
case UNARY_PLUS:
return PrefixOperator.PLUS;
case UNARY_MINUS:
return PrefixOperator.MINUS;
case BITWISE_COMPLEMENT:
return PrefixOperator.COMPLEMENT;
case LOGICAL_COMPLEMENT:
return PrefixOperator.NOT;
default:
return null;
}
}
@Nullable
static PostfixOperator getPostfixOperator(com.sun.source.tree.Tree.Kind operator) {
switch (operator) {
case POSTFIX_INCREMENT:
return PostfixOperator.INCREMENT;
case POSTFIX_DECREMENT:
return PostfixOperator.DECREMENT;
default:
return null;
}
}
@Nullable
static BinaryOperator getBinaryOperator(com.sun.source.tree.Tree.Kind operator) {
switch (operator) {
case ASSIGNMENT:
return BinaryOperator.ASSIGN;
case PLUS_ASSIGNMENT:
return BinaryOperator.PLUS_ASSIGN;
case MINUS_ASSIGNMENT:
return BinaryOperator.MINUS_ASSIGN;
case MULTIPLY_ASSIGNMENT:
return BinaryOperator.TIMES_ASSIGN;
case DIVIDE_ASSIGNMENT:
return BinaryOperator.DIVIDE_ASSIGN;
case AND_ASSIGNMENT:
return BinaryOperator.BIT_AND_ASSIGN;
case OR_ASSIGNMENT:
return BinaryOperator.BIT_OR_ASSIGN;
case XOR_ASSIGNMENT:
return BinaryOperator.BIT_XOR_ASSIGN;
case REMAINDER_ASSIGNMENT:
return BinaryOperator.REMAINDER_ASSIGN;
case LEFT_SHIFT_ASSIGNMENT:
return BinaryOperator.LEFT_SHIFT_ASSIGN;
case RIGHT_SHIFT_ASSIGNMENT:
return BinaryOperator.RIGHT_SHIFT_SIGNED_ASSIGN;
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
return BinaryOperator.RIGHT_SHIFT_UNSIGNED_ASSIGN;
case AND:
return BinaryOperator.BIT_AND;
case CONDITIONAL_AND:
return BinaryOperator.CONDITIONAL_AND;
case CONDITIONAL_OR:
return BinaryOperator.CONDITIONAL_OR;
case DIVIDE:
return BinaryOperator.DIVIDE;
case EQUAL_TO:
return BinaryOperator.EQUALS;
case GREATER_THAN:
return BinaryOperator.GREATER;
case GREATER_THAN_EQUAL:
return BinaryOperator.GREATER_EQUALS;
case LEFT_SHIFT:
return BinaryOperator.LEFT_SHIFT;
case LESS_THAN:
return BinaryOperator.LESS;
case LESS_THAN_EQUAL:
return BinaryOperator.LESS_EQUALS;
case MINUS:
return BinaryOperator.MINUS;
case MULTIPLY:
return BinaryOperator.TIMES;
case NOT_EQUAL_TO:
return BinaryOperator.NOT_EQUALS;
case OR:
return BinaryOperator.BIT_OR;
case PLUS:
return BinaryOperator.PLUS;
case REMAINDER:
return BinaryOperator.REMAINDER;
case RIGHT_SHIFT:
return BinaryOperator.RIGHT_SHIFT_SIGNED;
case UNSIGNED_RIGHT_SHIFT:
return BinaryOperator.RIGHT_SHIFT_UNSIGNED;
case XOR:
return BinaryOperator.BIT_XOR;
default:
return null;
}
}
Variable createVariable(
SourcePosition sourcePosition, VariableElement variableElement, boolean isParameter) {
TypeMirror type = variableElement.asType();
String name = variableElement.getSimpleName().toString();
TypeDescriptor typeDescriptor =
isParameter
? createTypeDescriptorWithNullability(
type, variableElement.getAnnotationMirrors(), /* inNullMarkedScope= */ false)
: createTypeDescriptor(type);
boolean isFinal = isFinal(variableElement);
boolean isUnusableByJsSuppressed =
JsInteropAnnotationUtils.isUnusableByJsSuppressed(variableElement);
return Variable.newBuilder()
.setName(name)
.setTypeDescriptor(typeDescriptor)
.setFinal(isFinal)
.setParameter(isParameter)
.setUnusableByJsSuppressed(isUnusableByJsSuppressed)
.setSourcePosition(sourcePosition)
.build();
}
DeclaredTypeDescriptor createDeclaredTypeDescriptor(TypeMirror typeMirror) {
return createDeclaredTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false);
}
DeclaredTypeDescriptor createDeclaredTypeDescriptor(
TypeMirror typeMirror, boolean inNullMarkedScope) {
return createTypeDescriptor(typeMirror, inNullMarkedScope, DeclaredTypeDescriptor.class);
}
/** Creates a specific subclass of TypeDescriptor from a TypeMirror. */
T createTypeDescriptor(TypeMirror typeMirror, Class clazz) {
return createTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false, clazz);
}
/** Creates a specific subclass of TypeDescriptor from a TypeMirror. */
T createTypeDescriptor(
TypeMirror typeMirror, boolean inNullMarkedScope, Class clazz) {
return clazz.cast(createTypeDescriptor(typeMirror, inNullMarkedScope));
}
/** Creates a TypeDescriptor from a TypeMirror. */
TypeDescriptor createTypeDescriptor(TypeMirror typeMirror) {
return createTypeDescriptor(typeMirror, /* inNullMarkedScope= */ false);
}
/** Creates a TypeDescriptor from a TypeMirror. */
TypeDescriptor createTypeDescriptor(TypeMirror typeMirror, boolean inNullMarkedScope) {
return createTypeDescriptorWithNullability(typeMirror, ImmutableList.of(), inNullMarkedScope);
}
/** Creates a type descriptor for the given TypeMirror, taking into account nullability. */
@Nullable
private TypeDescriptor createTypeDescriptorWithNullability(
TypeMirror typeMirror,
List extends AnnotationMirror> elementAnnotations,
boolean inNullMarkedScope) {
if (typeMirror == null || typeMirror.getKind() == TypeKind.NONE) {
return null;
}
if (typeMirror.getKind().isPrimitive() || typeMirror.getKind() == TypeKind.VOID) {
return PrimitiveTypes.get(asElement(typeMirror).getSimpleName().toString());
}
if (typeMirror.getKind() == TypeKind.INTERSECTION) {
return createIntersectionType((IntersectionClassType) typeMirror);
}
if (typeMirror.getKind() == TypeKind.UNION) {
return createUnionType((UnionClassType) typeMirror);
}
if (typeMirror.getKind() == TypeKind.NULL) {
return TypeDescriptors.get().javaLangObject;
}
if (typeMirror.getKind() == TypeKind.TYPEVAR) {
return createTypeVariable((javax.lang.model.type.TypeVariable) typeMirror);
}
if (typeMirror.getKind() == TypeKind.WILDCARD) {
return createWildcardTypeVariable(
((javax.lang.model.type.WildcardType) typeMirror).getExtendsBound());
}
boolean isNullable = isNullable(typeMirror, elementAnnotations, inNullMarkedScope);
if (typeMirror.getKind() == TypeKind.ARRAY) {
ArrayType arrayType = (ArrayType) typeMirror;
TypeDescriptor componentTypeDescriptor =
createTypeDescriptor(arrayType.getComponentType(), inNullMarkedScope);
return ArrayTypeDescriptor.newBuilder()
.setComponentTypeDescriptor(componentTypeDescriptor)
.setNullable(isNullable)
.build();
}
return withNullability(
createDeclaredType((ClassType) typeMirror, inNullMarkedScope), isNullable);
}
/**
* Returns whether the given type binding should be nullable, according to the annotations on it
* and if nullability is enabled for the package containing the binding.
*/
private boolean isNullable(
TypeMirror typeMirror,
List extends AnnotationMirror> elementAnnotations,
boolean inNullMarkedScope) {
checkArgument(!typeMirror.getKind().isPrimitive());
if (asTypeElement(typeMirror).getQualifiedName().contentEquals("java.lang.Void")) {
// Void is always nullable.
return true;
}
Iterable extends AnnotationMirror> allAnnotations =
Iterables.concat(elementAnnotations, typeMirror.getAnnotationMirrors());
for (AnnotationMirror annotationMirror : allAnnotations) {
if (isNonNullAnnotation(annotationMirror)) {
return false;
}
if (isNullableAnnotation(annotationMirror)) {
return true;
}
}
return !inNullMarkedScope;
}
private static boolean isNonNullAnnotation(AnnotationMirror annotation) {
Type annotationType = (Type) annotation.getAnnotationType();
return Nullability.isNonNullAnnotation(
annotationType.asElement().getQualifiedName().toString());
}
private static boolean isNullableAnnotation(AnnotationMirror annotation) {
Type annotationType = (Type) annotation.getAnnotationType();
return Nullability.isNullableAnnotation(
annotationType.asElement().getQualifiedName().toString());
}
private TypeVariable createTypeVariable(javax.lang.model.type.TypeVariable typeVariable) {
if (typeVariable instanceof CapturedType) {
return createWildcardTypeVariable(typeVariable.getUpperBound());
}
Supplier boundTypeDescriptorFactory =
() -> createTypeDescriptor(typeVariable.getUpperBound());
List classComponents = getClassComponents(typeVariable);
return TypeVariable.newBuilder()
.setUpperBoundTypeDescriptorFactory(boundTypeDescriptorFactory)
.setUniqueKey(
String.join("::", classComponents)
+ (typeVariable.getUpperBound() != null
? typeVariable.getUpperBound().toString()
: ""))
.setName(typeVariable.asElement().getSimpleName().toString())
.build();
}
private TypeVariable createWildcardTypeVariable(TypeMirror bound) {
return TypeVariable.newBuilder()
.setUpperBoundTypeDescriptorFactory(() -> createTypeDescriptor(bound))
.setWildcard(true)
.setName("?")
.setUniqueKey("::?::" + (bound != null ? bound.toString() : ""))
.build();
}
private static DeclaredTypeDescriptor withNullability(
DeclaredTypeDescriptor typeDescriptor, boolean nullable) {
return nullable ? typeDescriptor.toNullable() : typeDescriptor.toNonNullable();
}
/**
* In case the given type element is nested, return the outermost possible enclosing type element.
*/
private static TypeElement toTopLevelTypeBinding(Element element) {
if (element.getEnclosingElement().getKind() == ElementKind.PACKAGE) {
return (TypeElement) element;
}
return toTopLevelTypeBinding(element.getEnclosingElement());
}
private ImmutableList getClassComponents(
javax.lang.model.type.TypeVariable typeVariable) {
Element enclosingElement = typeVariable.asElement().getEnclosingElement();
if (enclosingElement.getKind() == ElementKind.CLASS
|| enclosingElement.getKind() == ElementKind.INTERFACE
|| enclosingElement.getKind() == ElementKind.ENUM) {
return ImmutableList.builder()
.addAll(getClassComponents(enclosingElement))
.add(
// If it is a class-level type variable, use the simple name (with prefix "C_") as the
// current name component.
"C_" + typeVariable.asElement().getSimpleName())
.build();
} else {
return ImmutableList.builder()
.addAll(getClassComponents(enclosingElement.getEnclosingElement()))
.add(
"M_"
+ enclosingElement.getSimpleName()
+ "_"
+ typeVariable.asElement().getSimpleName())
.build();
}
}
private ImmutableList getClassComponents(Element element) {
if (!(element instanceof TypeElement)) {
return ImmutableList.of();
}
TypeElement typeElement = (TypeElement) element;
List classComponents = new ArrayList<>();
TypeElement currentType = typeElement;
while (currentType != null) {
String simpleName;
if (currentType.getNestingKind() == NestingKind.LOCAL
|| currentType.getNestingKind() == NestingKind.ANONYMOUS) {
// JavaC binary name for local class is like package.components.EnclosingClass$1SimpleName
// Extract the generated name by taking the part after the binary name of the declaring
// class.
String binaryName = getBinaryNameFromTypeBinding(currentType);
String declaringClassPrefix =
getBinaryNameFromTypeBinding(getEnclosingType(currentType)) + "$";
simpleName = binaryName.substring(declaringClassPrefix.length());
} else {
simpleName = asElement(erasure(currentType.asType())).getSimpleName().toString();
}
classComponents.add(0, simpleName);
Element enclosingElement = currentType.getEnclosingElement();
while (enclosingElement != null
&& enclosingElement.getKind() != ElementKind.CLASS
&& enclosingElement.getKind() != ElementKind.INTERFACE
&& enclosingElement.getKind() != ElementKind.ENUM) {
enclosingElement = enclosingElement.getEnclosingElement();
}
currentType = (TypeElement) enclosingElement;
}
return ImmutableList.copyOf(classComponents);
}
/** Returns the binary name for a type element. */
private static String getBinaryNameFromTypeBinding(TypeElement typeElement) {
return ((ClassSymbol) typeElement).flatName().toString();
}
private boolean isEnumSyntheticMethod(ExecutableElement methodElement) {
// Enum synthetic methods are not marked as such because per JLS 13.1 these methods are
// implicitly declared but are not marked as synthetic.
return getEnclosingType(methodElement).getKind() == ElementKind.ENUM
&& (isValuesMethod(methodElement) || isValueOfMethod(methodElement));
}
private static boolean isValuesMethod(ExecutableElement methodElement) {
return methodElement.getSimpleName().contentEquals("values")
&& methodElement.getParameters().isEmpty();
}
private boolean isValueOfMethod(ExecutableElement methodElement) {
return methodElement.getSimpleName().contentEquals("valueOf")
&& methodElement.getParameters().size() == 1
&& asTypeElement(methodElement.getParameters().get(0).asType())
.getQualifiedName()
.contentEquals("java.lang.String");
}
/**
* Returns true if instances of this type capture its outer instances; i.e. if it is an non static
* member class, or an anonymous or local class defined in an instance context.
*/
static boolean capturesEnclosingInstance(ClassSymbol classSymbol) {
if (classSymbol.isAnonymous()) {
return classSymbol.hasOuterInstance() || !isStatic(classSymbol.getEnclosingElement());
}
return classSymbol.hasOuterInstance();
}
FieldDescriptor createFieldDescriptor(VariableElement variableElement) {
return createFieldDescriptor(variableElement, variableElement.asType());
}
FieldDescriptor createFieldDescriptor(VariableElement variableElement, TypeMirror type) {
boolean isStatic = isStatic(variableElement);
Visibility visibility = getVisibility(variableElement);
DeclaredTypeDescriptor enclosingTypeDescriptor =
createDeclaredTypeDescriptor(getEnclosingType(variableElement).asType());
String fieldName = variableElement.getSimpleName().toString();
TypeDescriptor thisTypeDescriptor =
createTypeDescriptorWithNullability(
type,
variableElement.getAnnotationMirrors(),
enclosingTypeDescriptor.getTypeDeclaration().isNullMarked());
boolean isEnumConstant = ((VarSymbol) variableElement).isEnum();
if (isEnumConstant) {
// Enum fields are always non-nullable.
thisTypeDescriptor = thisTypeDescriptor.toNonNullable();
}
FieldDescriptor declarationFieldDescriptor = null;
if (!javacTypes.isSameType(variableElement.asType(), type)) {
// Field references might be parameterized, and when they are we set the declaration
// descriptor.
declarationFieldDescriptor = createFieldDescriptor(variableElement, variableElement.asType());
}
JsInfo jsInfo = JsInteropUtils.getJsInfo(variableElement);
Object constantValue = variableElement.getConstantValue();
boolean isCompileTimeConstant = constantValue != null;
if (isCompileTimeConstant) {
thisTypeDescriptor = thisTypeDescriptor.toNonNullable();
}
boolean isFinal = isFinal(variableElement);
return FieldDescriptor.newBuilder()
.setEnclosingTypeDescriptor(enclosingTypeDescriptor)
.setName(fieldName)
.setTypeDescriptor(thisTypeDescriptor)
.setStatic(isStatic)
.setVisibility(visibility)
.setOriginalJsInfo(jsInfo)
.setFinal(isFinal)
.setCompileTimeConstant(isCompileTimeConstant)
.setConstantValue(
constantValue != null ? Literal.fromValue(constantValue, thisTypeDescriptor) : null)
.setDeclarationDescriptor(declarationFieldDescriptor)
.setEnumConstant(isEnumConstant)
.setUnusableByJsSuppressed(
JsInteropAnnotationUtils.isUnusableByJsSuppressed(variableElement))
.setDeprecated(isDeprecated(variableElement))
.build();
}
/**
* Creates a MethodDescriptor from javac internal representation.
*
* @param methodType an ExecutableType containing the (inferred) specialization of the method in a
* usage location.
* @param returnType the (inferred) specialized return type.
* @param declarationMethodElement the method declaration.
*/
MethodDescriptor createMethodDescriptor(
ExecutableType methodType, Type returnType, ExecutableElement declarationMethodElement) {
DeclaredTypeDescriptor enclosingTypeDescriptor =
createDeclaredTypeDescriptor(declarationMethodElement.getEnclosingElement().asType());
MethodDescriptor declarationMethodDescriptor = null;
List extends TypeMirror> parameterTypes = methodType.getParameterTypes();
if (isSpecialized(declarationMethodElement, parameterTypes, returnType)) {
declarationMethodDescriptor = createDeclarationMethodDescriptor(declarationMethodElement);
}
TypeDescriptor returnTypeDescriptor =
applyReturnTypeNullabilityAnnotations(
createTypeDescriptorWithNullability(
returnType,
declarationMethodElement.getAnnotationMirrors(),
enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()),
declarationMethodElement);
ImmutableList.Builder parametersBuilder = ImmutableList.builder();
for (int i = 0; i < parameterTypes.size(); i++) {
parametersBuilder.add(
applyParameterNullabilityAnnotations(
createTypeDescriptorWithNullability(
parameterTypes.get(i),
declarationMethodElement.getParameters().get(i).getAnnotationMirrors(),
enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()),
declarationMethodElement,
i));
}
// generate type parameters declared in the method.
return createDeclaredMethodDescriptor(
enclosingTypeDescriptor.toNullable(),
declarationMethodElement,
declarationMethodDescriptor,
parametersBuilder.build(),
returnTypeDescriptor);
}
/** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */
MethodDescriptor createMethodDescriptor(
DeclaredTypeDescriptor enclosingTypeDescriptor,
ExecutableElement methodElement,
ExecutableElement declarationMethodElement) {
MethodDescriptor declarationMethodDescriptor = null;
ImmutableList parameters =
methodElement.getParameters().stream()
.map(VariableElement::asType)
.collect(toImmutableList());
TypeMirror returnType = methodElement.getReturnType();
if (isSpecialized(declarationMethodElement, parameters, returnType)) {
declarationMethodDescriptor =
createDeclarationMethodDescriptor(
declarationMethodElement, enclosingTypeDescriptor.getDeclarationDescriptor());
}
TypeDescriptor returnTypeDescriptor =
applyReturnTypeNullabilityAnnotations(
createTypeDescriptorWithNullability(
returnType,
declarationMethodElement.getAnnotationMirrors(),
enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()),
declarationMethodElement);
ImmutableList.Builder parametersBuilder = ImmutableList.builder();
for (int i = 0; i < parameters.size(); i++) {
parametersBuilder.add(
applyParameterNullabilityAnnotations(
createTypeDescriptorWithNullability(
parameters.get(i),
declarationMethodElement.getParameters().get(i).getAnnotationMirrors(),
enclosingTypeDescriptor.getTypeDeclaration().isNullMarked()),
declarationMethodElement,
i));
}
return createDeclaredMethodDescriptor(
enclosingTypeDescriptor.toNullable(),
declarationMethodElement,
declarationMethodDescriptor,
parametersBuilder.build(),
returnTypeDescriptor);
}
/** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */
MethodDescriptor createDeclarationMethodDescriptor(ExecutableElement methodElement) {
DeclaredTypeDescriptor enclosingTypeDescriptor =
createDeclaredTypeDescriptor(methodElement.getEnclosingElement().asType());
return createDeclarationMethodDescriptor(methodElement, enclosingTypeDescriptor);
}
/** Create a MethodDescriptor directly based on the given JavaC ExecutableElement. */
MethodDescriptor createDeclarationMethodDescriptor(
ExecutableElement methodElement, DeclaredTypeDescriptor enclosingTypeDescriptor) {
return createMethodDescriptor(enclosingTypeDescriptor, methodElement, methodElement);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Utility methods to process nullability annotations on classes that are compiled separately.
// Javac does not present TYPE_USE annotation in the returned type instances.
private static TypeDescriptor applyParameterNullabilityAnnotations(
TypeDescriptor typeDescriptor, ExecutableElement declarationMethodElement, int index) {
return applyNullabilityAnnotations(
typeDescriptor,
declarationMethodElement,
position ->
position.parameter_index == index
&& position.type == TargetType.METHOD_FORMAL_PARAMETER);
}
private static TypeDescriptor applyReturnTypeNullabilityAnnotations(
TypeDescriptor typeDescriptor, ExecutableElement declarationMethodElement) {
return applyNullabilityAnnotations(
typeDescriptor,
declarationMethodElement,
position -> position.type == TargetType.METHOD_RETURN);
}
private static TypeDescriptor applyNullabilityAnnotations(
TypeDescriptor typeDescriptor,
Element declarationMethodElement,
Predicate positionSelector) {
List methodAnnotations =
((Symbol) declarationMethodElement).getRawTypeAttributes();
for (TypeCompound methodAnnotation : methodAnnotations) {
TypeAnnotationPosition position = methodAnnotation.getPosition();
if (!positionSelector.test(position)) {
continue;
}
if (isNonNullAnnotation(methodAnnotation)) {
typeDescriptor =
applyNullabilityAnnotation(typeDescriptor, position.location, /* isNullable= */ false);
} else if (isNullableAnnotation(methodAnnotation)) {
typeDescriptor =
applyNullabilityAnnotation(typeDescriptor, position.location, /* isNullable= */ true);
}
}
return typeDescriptor;
}
private static TypeDescriptor applyNullabilityAnnotation(
TypeDescriptor typeDescriptor, List location, boolean isNullable) {
if (location.isEmpty()) {
if (TypeDescriptors.isJavaLangVoid(typeDescriptor)) {
return typeDescriptor;
}
return isNullable ? typeDescriptor.toNullable() : typeDescriptor.toNonNullable();
}
TypePathEntry currentEntry = location.get(0);
List rest = location.subList(1, location.size());
switch (currentEntry.tag) {
case TYPE_ARGUMENT:
DeclaredTypeDescriptor declaredTypeDescriptor = (DeclaredTypeDescriptor) typeDescriptor;
List replacements =
new ArrayList<>(declaredTypeDescriptor.getTypeArgumentDescriptors());
if (currentEntry.arg < replacements.size()) {
// Only apply the type argument annotation if the type is not raw.
replacements.set(
currentEntry.arg,
applyNullabilityAnnotation(replacements.get(currentEntry.arg), rest, isNullable));
}
return declaredTypeDescriptor.withTypeArguments(replacements);
case ARRAY:
ArrayTypeDescriptor arrayTypeDescriptor = (ArrayTypeDescriptor) typeDescriptor;
return ArrayTypeDescriptor.newBuilder()
.setComponentTypeDescriptor(
applyNullabilityAnnotation(
arrayTypeDescriptor.getComponentTypeDescriptor(), rest, isNullable))
.setNullable(typeDescriptor.isNullable())
.build();
case INNER_TYPE:
DeclaredTypeDescriptor innerType = (DeclaredTypeDescriptor) typeDescriptor;
// Consume all inner type annotation and only continue if does not relate to an outer type
// of the type in question.
int innerDepth = getInnerDepth(innerType);
int innerCount = countInner(rest) + 1;
if (innerCount != innerDepth) {
// Applies to outer type, not relevant for nullability, ignore.
return innerType;
}
return applyNullabilityAnnotation(
typeDescriptor, rest.subList(innerCount - 1, rest.size()), isNullable);
case WILDCARD:
TypeVariable typeVariable = (TypeVariable) typeDescriptor;
return TypeVariable.createWildcardWithUpperBound(
applyNullabilityAnnotation(
typeVariable.getUpperBoundTypeDescriptor(), rest, isNullable));
}
return typeDescriptor;
}
private static int countInner(List rest) {
return !rest.isEmpty() && rest.get(0).tag == TypePathEntryKind.INNER_TYPE
? countInner(rest.subList(1, rest.size())) + 1
: 0;
}
private static int getInnerDepth(DeclaredTypeDescriptor innerType) {
if (innerType.getTypeDeclaration().isCapturingEnclosingInstance()) {
return getInnerDepth(innerType.getEnclosingTypeDescriptor()) + 1;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns true if any of the type parameters has been specialized.
*
* For example the type {@code List} specialized the type variable {@code T} from the
* class declaration.
*/
private boolean isSpecialized(
ExecutableElement declarationMethodElement,
List extends TypeMirror> parameters,
TypeMirror returnType) {
return !isSameType(returnType, declarationMethodElement.getReturnType())
|| !Streams.zip(
parameters.stream(),
declarationMethodElement.getParameters().stream(),
(thisType, thatType) -> isSameType(thisType, thatType.asType()))
.allMatch(equals -> equals);
}
private boolean isSameType(TypeMirror thisType, TypeMirror thatType) {
return internalTypes.isSameType((Type) thisType, (Type) thatType);
}
private MethodDescriptor createDeclaredMethodDescriptor(
DeclaredTypeDescriptor enclosingTypeDescriptor,
ExecutableElement declarationMethodElement,
MethodDescriptor declarationMethodDescriptor,
List parameters,
TypeDescriptor returnTypeDescriptor) {
ImmutableList typeParameterTypeDescriptors =
declarationMethodElement.getTypeParameters().stream()
.map(Element::asType)
.map(this::createTypeDescriptor)
.map(TypeVariable.class::cast)
.collect(toImmutableList());
boolean isStatic = isStatic(declarationMethodElement);
Visibility visibility = getVisibility(declarationMethodElement);
boolean isDefault = isDefaultMethod(declarationMethodElement);
JsInfo jsInfo = JsInteropUtils.getJsInfo(declarationMethodElement);
boolean isNative =
isNative(declarationMethodElement)
|| (!jsInfo.isJsOverlay()
&& enclosingTypeDescriptor.isNative()
&& isAbstract(declarationMethodElement));
boolean isConstructor = declarationMethodElement.getKind() == ElementKind.CONSTRUCTOR;
String methodName = declarationMethodElement.getSimpleName().toString();
ImmutableList.Builder parameterDescriptorBuilder = ImmutableList.builder();
for (int i = 0; i < parameters.size(); i++) {
parameterDescriptorBuilder.add(
ParameterDescriptor.newBuilder()
.setTypeDescriptor(parameters.get(i))
.setJsOptional(JsInteropUtils.isJsOptional(declarationMethodElement, i))
.setVarargs(i == parameters.size() - 1 && declarationMethodElement.isVarArgs())
.setDoNotAutobox(JsInteropUtils.isDoNotAutobox(declarationMethodElement, i))
.build());
}
boolean hasUncheckedCast = hasUncheckedCastAnnotation(declarationMethodElement);
return MethodDescriptor.newBuilder()
.setEnclosingTypeDescriptor(enclosingTypeDescriptor)
.setName(isConstructor ? null : methodName)
.setParameterDescriptors(parameterDescriptorBuilder.build())
.setDeclarationDescriptor(declarationMethodDescriptor)
.setReturnTypeDescriptor(isConstructor ? enclosingTypeDescriptor : returnTypeDescriptor)
.setTypeParameterTypeDescriptors(typeParameterTypeDescriptors)
.setOriginalJsInfo(jsInfo)
.setVisibility(visibility)
.setStatic(isStatic)
.setConstructor(isConstructor)
.setNative(isNative)
.setWasmInfo(getWasmInfo(declarationMethodElement))
.setFinal(isFinal(declarationMethodElement))
.setDefaultMethod(isDefault)
.setAbstract(isAbstract(declarationMethodElement))
.setSynthetic(isSynthetic(declarationMethodElement))
.setEnumSyntheticMethod(isEnumSyntheticMethod(declarationMethodElement))
.setSideEffectFree(isAnnotatedWithHasNoSideEffects(declarationMethodElement))
.setUnusableByJsSuppressed(
JsInteropAnnotationUtils.isUnusableByJsSuppressed(declarationMethodElement))
.setDeprecated(isDeprecated(declarationMethodElement))
.setUncheckedCast(hasUncheckedCast)
.build();
}
/** Returns true if the element is annotated with @UncheckedCast. */
private static boolean hasUncheckedCastAnnotation(Element element) {
return AnnotationUtils.hasAnnotation(element, UNCHECKED_CAST_ANNOTATION_NAME);
}
/** Returns true if the element is annotated with @HasNoSideEffects. */
private static boolean isAnnotatedWithHasNoSideEffects(Element element) {
return AnnotationUtils.hasAnnotation(element, HAS_NO_SIDE_EFFECTS_ANNOTATION_NAME);
}
private boolean isJavaLangObjectOverride(MethodSymbol method) {
return getJavaLangObjectMethods().stream()
.anyMatch(
om ->
method.getSimpleName().equals(om.name)
&& javacTypes.isSubsignature(
(ExecutableType) method.asType(), (ExecutableType) om.asType()));
}
private ImmutableSet getJavaLangObjectMethods() {
ClassType javaLangObjectTypeBinding =
(ClassType) elements.getTypeElement("java.lang.Object").asType();
return getDeclaredMethods(javaLangObjectTypeBinding).stream()
.map(MethodDeclarationPair::getMethodSymbol)
.filter(JavaEnvironment::isPolymorphic)
.collect(ImmutableSet.toImmutableSet());
}
private static boolean isPolymorphic(MethodSymbol method) {
return !method.isConstructor()
&& !isStatic(method)
&& !method.getModifiers().contains(Modifier.PRIVATE);
}
public ImmutableList createTypeDescriptors(
List extends TypeMirror> typeMirrors, boolean inNullMarkedScope) {
return typeMirrors.stream()
.map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope))
.collect(toImmutableList());
}
public ImmutableList createTypeDescriptors(
List extends TypeMirror> typeMirrors,
boolean inNullMarkedScope,
Class clazz,
Element declarationElement) {
ImmutableList.Builder typeDescriptorsBuilder = ImmutableList.builder();
for (int i = 0; i < typeMirrors.size(); i++) {
final int index = i;
typeDescriptorsBuilder.add(
clazz.cast(
applyNullabilityAnnotations(
createTypeDescriptor(typeMirrors.get(i), inNullMarkedScope, clazz),
declarationElement,
position ->
position.type == TargetType.CLASS_EXTENDS && position.type_index == index)));
}
return typeDescriptorsBuilder.build();
}
public ImmutableList createTypeDescriptors(
List extends TypeMirror> typeMirrors, boolean inNullMarkedScope, Class clazz) {
return typeMirrors.stream()
.map(typeMirror -> createTypeDescriptor(typeMirror, inNullMarkedScope, clazz))
.collect(toImmutableList());
}
private TypeElement getTypeElement(String qualifiedSourceName) {
return elements.getTypeElement(qualifiedSourceName);
}
private Element asElement(TypeMirror typeMirror) {
if (typeMirror instanceof JCPrimitiveType) {
return ((JCPrimitiveType) typeMirror).asElement();
}
if (typeMirror instanceof Type) {
return ((Type) typeMirror).tsym;
}
return javacTypes.asElement(typeMirror);
}
private TypeElement asTypeElement(TypeMirror typeMirror) {
return (TypeElement) asElement(typeMirror);
}
private TypeMirror erasure(TypeMirror typeMirror) {
return javacTypes.erasure(typeMirror);
}
private PackageElement getPackageOf(TypeElement typeElement) {
return elements.getPackageOf(typeElement);
}
private TypeDescriptor createIntersectionType(IntersectionClassType intersectionType) {
ImmutableList intersectedTypeDescriptors =
createTypeDescriptors(
intersectionType.getBounds(), /* inNullMarkedScope= */ false, TypeDescriptor.class);
return IntersectionTypeDescriptor.newBuilder()
.setIntersectionTypeDescriptors(intersectedTypeDescriptors)
.build();
}
private TypeDescriptor createUnionType(UnionClassType unionType) {
ImmutableList unionTypeDescriptors =
createTypeDescriptors(unionType.getAlternatives(), /* inNullMarkedScope= */ false);
return UnionTypeDescriptor.newBuilder().setUnionTypeDescriptors(unionTypeDescriptors).build();
}
private DeclaredTypeDescriptor createDeclaredType(
final DeclaredType classType, boolean inNullMarkedScope) {
DeclaredTypeDescriptor cachedTypeDescriptor =
getCachedTypeDescriptor(classType, inNullMarkedScope);
if (cachedTypeDescriptor != null) {
return cachedTypeDescriptor;
}
DeclaredTypeDescriptor typeDescriptor =
createDeclarationForType((TypeElement) classType.asElement())
.toDescriptor(createTypeDescriptors(getTypeArguments(classType), inNullMarkedScope));
putTypeDescriptorInCache(inNullMarkedScope, classType, typeDescriptor);
return typeDescriptor;
}
private final Map
cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope = new HashMap<>();
private final Map
cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope = new HashMap<>();
private DeclaredTypeDescriptor getCachedTypeDescriptor(
DeclaredType classType, boolean inNullMarkedScope) {
Map cache =
inNullMarkedScope
? cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope
: cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope;
return cache.get(classType);
}
private void putTypeDescriptorInCache(
boolean inNullMarkedScope, DeclaredType classType, DeclaredTypeDescriptor typeDescriptor) {
Map cache =
inNullMarkedScope
? cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope
: cachedDeclaredTypeDescriptorByDeclaredTypeOutOfNullMarkedScope;
cache.put(classType, typeDescriptor);
}
private static List getTypeArguments(DeclaredType declaredType) {
List typeArguments = new ArrayList<>();
DeclaredType currentType = declaredType;
do {
typeArguments.addAll(currentType.getTypeArguments());
Element enclosingElement = currentType.asElement().getEnclosingElement();
if (enclosingElement.getKind() == ElementKind.METHOD
|| enclosingElement.getKind() == ElementKind.CONSTRUCTOR) {
typeArguments.addAll(
((Parameterizable) enclosingElement)
.getTypeParameters().stream().map(Element::asType).collect(toImmutableList()));
}
currentType =
currentType.getEnclosingType() instanceof DeclaredType
? (DeclaredType) currentType.getEnclosingType()
: null;
} while (currentType != null);
return typeArguments;
}
private ImmutableList getDeclaredMethods(ClassType classType) {
return classType.asElement().getEnclosedElements().stream()
.filter(
element ->
!isSynthetic(element)
&& (element.getKind() == ElementKind.METHOD
|| element.getKind() == ElementKind.CONSTRUCTOR))
.map(MethodSymbol.class::cast)
.map(
methodSymbol ->
new MethodDeclarationPair(
(MethodSymbol) methodSymbol.asMemberOf(classType, internalTypes), methodSymbol))
.collect(toImmutableList());
}
static final class MethodDeclarationPair {
private final MethodSymbol methodSymbol;
private final MethodSymbol declarationMethodSymbol;
private MethodDeclarationPair(MethodSymbol methodSymbol, MethodSymbol declarationMethodSymbol) {
this.methodSymbol = methodSymbol;
this.declarationMethodSymbol = declarationMethodSymbol;
}
public MethodSymbol getMethodSymbol() {
return methodSymbol;
}
public MethodSymbol getDeclarationMethodSymbol() {
return declarationMethodSymbol;
}
}
private ImmutableList getMethods(ClassType classType) {
return elements.getAllMembers((TypeElement) classType.asElement()).stream()
.filter(
element ->
!isSynthetic(element)
&& (element.getKind() == ElementKind.METHOD
|| element.getKind() == ElementKind.CONSTRUCTOR))
.map(MethodSymbol.class::cast)
.map(
methodSymbol ->
new MethodDeclarationPair(
(MethodSymbol) methodSymbol.asMemberOf(classType, internalTypes), methodSymbol))
.collect(toImmutableList());
}
private static Kind getKindFromTypeBinding(TypeElement typeElement) {
if (isEnum(typeElement) && !isAnonymous(typeElement)) {
// Do not consider the anonymous classes that constitute enum values as Enums, only the
// enum "class" itself is considered Kind.ENUM.
return Kind.ENUM;
} else if (isClass(typeElement) || (isEnum(typeElement) && isAnonymous(typeElement))) {
return Kind.CLASS;
} else if (isInterface(typeElement)) {
return Kind.INTERFACE;
}
throw new InternalCompilerError("Type binding %s not handled.", typeElement);
}
@Nullable
TypeDeclaration createDeclarationForType(final TypeElement typeElement) {
if (typeElement == null) {
return null;
}
PackageInfoCache packageInfoCache = PackageInfoCache.get();
// Compute these first since they're reused in other calculations.
boolean isAbstract = isAbstract(typeElement) && !isInterface(typeElement);
Kind kind = getKindFromTypeBinding(typeElement);
// TODO(b/341721484): Even though enums can not have the final modifier, turbine make them final
// in the header jars.
boolean isFinal = isFinal(typeElement) && kind != Kind.ENUM;
Supplier> declaredMethods =
() -> {
ImmutableList.Builder listBuilder = ImmutableList.builder();
for (MethodSymbol methodElement :
typeElement.getEnclosedElements().stream()
.filter(
element ->
element.getKind() == ElementKind.METHOD
|| element.getKind() == ElementKind.CONSTRUCTOR)
.map(MethodSymbol.class::cast)
.collect(toImmutableList())) {
MethodDescriptor methodDescriptor = createDeclarationMethodDescriptor(methodElement);
listBuilder.add(methodDescriptor);
}
return listBuilder.build();
};
DescriptorFactory singleAbstractMethod =
typeDeclaration -> {
if (kind != Kind.INTERFACE) {
return null;
}
// Get the declaration, possibly in a supertype.
var declaration =
createDeclarationMethodDescriptor(
getFunctionalInterfaceMethodDecl(typeElement.asType()));
return declaration == null
? null
// Find the parameterized version in the type.
: typeDeclaration.toDescriptor().getPolymorphicMethods().stream()
.filter(m -> m.getDeclarationDescriptor() == declaration)
.collect(onlyElement());
};
Supplier> declaredFields =
() ->
typeElement.getEnclosedElements().stream()
.filter(
element ->
element.getKind() == ElementKind.FIELD
|| element.getKind() == ElementKind.ENUM_CONSTANT)
.map(VariableElement.class::cast)
.map(this::createFieldDescriptor)
.collect(toImmutableList());
JsEnumInfo jsEnumInfo = JsInteropUtils.getJsEnumInfo(typeElement);
List typeParameterElements = getTypeParameters(typeElement);
boolean isNullMarked = isNullMarked(typeElement, packageInfoCache);
return TypeDeclaration.newBuilder()
.setClassComponents(getClassComponents(typeElement))
.setEnclosingTypeDeclaration(createDeclarationForType(getEnclosingType(typeElement)))
.setInterfaceTypeDescriptorsFactory(
() ->
createTypeDescriptors(
typeElement.getInterfaces(),
isNullMarked,
DeclaredTypeDescriptor.class,
typeElement))
.setHasAbstractModifier(isAbstract)
.setKind(kind)
.setAnnotation(isAnnotation(typeElement))
.setCapturingEnclosingInstance(capturesEnclosingInstance((ClassSymbol) typeElement))
.setFinal(isFinal)
.setFunctionalInterface(isFunctionalInterface(typeElement.asType()))
.setJsFunctionInterface(JsInteropUtils.isJsFunction(typeElement))
.setJsType(JsInteropUtils.isJsType(typeElement))
.setJsEnumInfo(jsEnumInfo)
.setNative(JsInteropUtils.isJsNativeType(typeElement))
.setAnonymous(isAnonymous(typeElement))
.setLocal(isLocal(typeElement))
.setSimpleJsName(JsInteropAnnotationUtils.getJsName(typeElement))
.setCustomizedJsNamespace(JsInteropAnnotationUtils.getJsNamespace(typeElement))
.setWasmInfo(getWasmInfo(typeElement))
.setNullMarked(isNullMarked)
.setOriginalSimpleSourceName(
typeElement.getSimpleName() != null ? typeElement.getSimpleName().toString() : null)
.setPackage(createPackageDeclaration(getPackageOf(typeElement), packageInfoCache))
.setSuperTypeDescriptorFactory(
() ->
(DeclaredTypeDescriptor)
applyNullabilityAnnotations(
createDeclaredTypeDescriptor(typeElement.getSuperclass(), isNullMarked),
typeElement,
position ->
position.type == TargetType.CLASS_EXTENDS && position.type_index == -1))
.setTypeParameterDescriptors(
typeParameterElements.stream()
.map(TypeParameterElement::asType)
.map(javax.lang.model.type.TypeVariable.class::cast)
.map(this::createTypeVariable)
.collect(toImmutableList()))
.setVisibility(getVisibility(typeElement))
.setDeclaredMethodDescriptorsFactory(declaredMethods)
.setSingleAbstractMethodDescriptorFactory(singleAbstractMethod)
.setDeclaredFieldDescriptorsFactory(declaredFields)
.setUnusableByJsSuppressed(JsInteropAnnotationUtils.isUnusableByJsSuppressed(typeElement))
.setDeprecated(isDeprecated(typeElement))
.build();
}
private static PackageDeclaration createPackageDeclaration(
PackageElement packageElement, PackageInfoCache packageInfoCache) {
// Caching is left to PackageDeclaration.Builder since construction is trivial.
String packageName = packageElement.getQualifiedName().toString();
return PackageDeclaration.newBuilder()
.setName(packageName)
.setCustomizedJsNamespace(packageInfoCache.getJsNamespace(packageName))
.build();
}
private static boolean isNullMarked(TypeElement classSymbol, PackageInfoCache packageInfoCache) {
if (packageInfoCache.isNullMarked(
getBinaryNameFromTypeBinding(toTopLevelTypeBinding(classSymbol)))) {
// The package is NullMarked, no need to look further.
return true;
}
return hasNullMarkedAnnotation(classSymbol);
}
private static boolean hasNullMarkedAnnotation(TypeElement classSymbol) {
if (classSymbol.getAnnotationMirrors().stream()
.anyMatch(a -> Nullability.isNullMarkedAnnotation(AnnotationUtils.getAnnotationName(a)))) {
// The type is NullMarked, no need to look further.
return true;
}
Element enclosingElement = classSymbol.getEnclosingElement();
return enclosingElement instanceof TypeElement
&& hasNullMarkedAnnotation((TypeElement) enclosingElement);
}
private static List getTypeParameters(TypeElement typeElement) {
List typeParameterElements =
new ArrayList<>(typeElement.getTypeParameters());
Element currentElement = typeElement;
Element enclosingElement = typeElement.getEnclosingElement();
while (enclosingElement != null) {
if (isStatic(currentElement)) {
break;
}
if (enclosingElement.getKind() != ElementKind.STATIC_INIT
&& enclosingElement.getKind() != ElementKind.INSTANCE_INIT
&& enclosingElement instanceof Parameterizable) {
// Add the enclosing element type variables, skip STATIC_INIT and INSTANCE_INIT since they
// never define type variables, and throw NPE if getTypeParameters is called on them.
typeParameterElements.addAll(((Parameterizable) enclosingElement).getTypeParameters());
}
currentElement = enclosingElement;
enclosingElement = enclosingElement.getEnclosingElement();
}
return typeParameterElements;
}
public static TypeElement getEnclosingType(Element typeElement) {
Element enclosing = typeElement.getEnclosingElement();
while (enclosing != null && !(enclosing instanceof TypeElement)) {
enclosing = enclosing.getEnclosingElement();
}
return (TypeElement) enclosing;
}
private static TypeElement getEnclosingType(TypeElement typeElement) {
Element enclosing = typeElement.getEnclosingElement();
while (enclosing != null && !(enclosing instanceof TypeElement)) {
enclosing = enclosing.getEnclosingElement();
}
return (TypeElement) enclosing;
}
@Nullable
private TypeMirror getFunctionalInterface(Type type) {
if (type.isIntersection()) {
return ((IntersectionType) type)
.getBounds().stream().filter(this::isFunctionalInterface).findFirst().orElse(null);
}
checkArgument(isFunctionalInterface(type));
return type;
}
MethodDescriptor getJsFunctionMethodDescriptor(TypeMirror type) {
DeclaredTypeDescriptor expressionTypeDescriptor =
createDeclaredTypeDescriptor(getFunctionalInterface((Type) type));
return createMethodDescriptor(
expressionTypeDescriptor,
(MethodSymbol) getFunctionalInterfaceMethod(type).asMemberOf((Type) type, internalTypes),
getFunctionalInterfaceMethod(type));
}
@Nullable
private MethodSymbol getFunctionalInterfaceMethodDecl(TypeMirror typeMirror) {
return Optional.ofNullable(getFunctionalInterfaceMethodPair(typeMirror))
.map(MethodDeclarationPair::getDeclarationMethodSymbol)
.orElse(null);
}
@Nullable
private MethodSymbol getFunctionalInterfaceMethod(TypeMirror typeMirror) {
return Optional.ofNullable(getFunctionalInterfaceMethodPair(typeMirror))
.map(MethodDeclarationPair::getMethodSymbol)
.orElse(null);
}
@Nullable
private MethodDeclarationPair getFunctionalInterfaceMethodPair(TypeMirror typeMirror) {
Type type = (Type) typeMirror;
if (!internalTypes.isFunctionalInterface(type)) {
return null;
}
if (type.isIntersection()) {
return ((IntersectionType) type)
.getBounds().stream()
.filter(this::isFunctionalInterface)
.map(this::getFunctionalInterfaceMethodPair)
.findFirst()
.orElse(null);
}
return getMethods((ClassType) type).stream()
.filter(
p ->
isAbstract(p.getDeclarationMethodSymbol())
&& !isJavaLangObjectOverride(p.getDeclarationMethodSymbol()))
.findFirst()
.orElse(null);
}
private boolean isFunctionalInterface(TypeMirror type) {
return internalTypes.isFunctionalInterface((Type) type)
&& ((Type) type).asElement().getKind() == ElementKind.INTERFACE;
}
private static boolean isEnum(TypeElement typeElement) {
return typeElement.getKind() == ElementKind.ENUM;
}
private static boolean isAnnotation(TypeElement typeElement) {
return typeElement.getKind() == ElementKind.ANNOTATION_TYPE;
}
private static boolean isAnonymous(TypeElement typeElement) {
return typeElement.getNestingKind() == NestingKind.ANONYMOUS;
}
private static boolean isClass(TypeElement typeElement) {
return typeElement.getKind() == ElementKind.CLASS;
}
private static boolean isInterface(TypeElement typeElement) {
return typeElement.getKind() == ElementKind.INTERFACE
|| typeElement.getKind() == ElementKind.ANNOTATION_TYPE;
}
private static boolean isLocal(TypeElement typeElement) {
return typeElement.getNestingKind() == NestingKind.LOCAL;
}
public static Visibility getVisibility(Element element) {
if (element.getModifiers().contains(Modifier.PUBLIC)) {
return Visibility.PUBLIC;
} else if (element.getModifiers().contains(Modifier.PROTECTED)) {
return Visibility.PROTECTED;
} else if (element.getModifiers().contains(Modifier.PRIVATE)) {
return Visibility.PRIVATE;
} else {
return Visibility.PACKAGE_PRIVATE;
}
}
@Nullable
private static String getWasmInfo(Element element) {
AnnotationMirror wasmAnnotation =
AnnotationUtils.findAnnotationBindingByName(
element.getAnnotationMirrors(), WASM_ANNOTATION_NAME);
if (wasmAnnotation == null) {
return null;
}
return AnnotationUtils.getAnnotationParameterString(wasmAnnotation, "value");
}
private static boolean isDeprecated(AnnotatedConstruct binding) {
return AnnotationUtils.hasAnnotation(binding, Deprecated.class.getName());
}
private static boolean isDefaultMethod(Element element) {
return element.getModifiers().contains(Modifier.DEFAULT);
}
private static boolean isAbstract(Element element) {
return element.getModifiers().contains(Modifier.ABSTRACT);
}
private static boolean isFinal(Element element) {
return element.getModifiers().contains(Modifier.FINAL);
}
public static boolean isStatic(Element element) {
return element.getModifiers().contains(Modifier.STATIC);
}
private static boolean isNative(Element element) {
return element.getModifiers().contains(Modifier.NATIVE);
}
private static boolean isSynthetic(Element element) {
return element instanceof Symbol && (((Symbol) element).flags() & Flags.SYNTHETIC) != 0;
}
}