1 /* 2 * Copyright (C) 2019 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.kotlin; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 20 import static com.google.auto.common.MoreElements.isAnnotationPresent; 21 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement; 22 import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT; 23 import static kotlinx.metadata.Flag.Class.IS_DATA; 24 import static kotlinx.metadata.Flag.Class.IS_OBJECT; 25 import static kotlinx.metadata.Flag.IS_PRIVATE; 26 27 import com.google.common.collect.ImmutableCollection; 28 import com.google.common.collect.ImmutableList; 29 import dagger.internal.codegen.extension.DaggerCollectors; 30 import java.lang.annotation.Annotation; 31 import java.util.Optional; 32 import javax.inject.Inject; 33 import javax.lang.model.element.AnnotationMirror; 34 import javax.lang.model.element.Element; 35 import javax.lang.model.element.ExecutableElement; 36 import javax.lang.model.element.TypeElement; 37 import javax.lang.model.element.VariableElement; 38 import javax.lang.model.util.ElementFilter; 39 import kotlin.Metadata; 40 import kotlin.jvm.JvmStatic; 41 import kotlinx.metadata.Flag; 42 43 /** Utility class for interacting with Kotlin Metadata. */ 44 public final class KotlinMetadataUtil { 45 46 private final KotlinMetadataFactory metadataFactory; 47 48 @Inject KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)49 KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) { 50 this.metadataFactory = metadataFactory; 51 } 52 53 /** 54 * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in 55 * an element that does. 56 */ hasMetadata(Element element)57 public boolean hasMetadata(Element element) { 58 return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class); 59 } 60 61 /** 62 * Returns the synthetic annotations of a Kotlin property. 63 * 64 * <p>Note that this method only looks for additional annotations in the synthetic property 65 * method, if any, of a Kotlin property and not for annotations in its backing field. 66 */ getSyntheticPropertyAnnotations( VariableElement fieldElement, Class<? extends Annotation> annotationType)67 public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations( 68 VariableElement fieldElement, Class<? extends Annotation> annotationType) { 69 return metadataFactory 70 .create(fieldElement) 71 .getSyntheticAnnotationMethod(fieldElement) 72 .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList()) 73 .orElse(ImmutableList.of()); 74 } 75 76 /** 77 * Returns {@code true} if the synthetic method for annotations is missing. This can occur when 78 * the Kotlin metadata of the property reports that it contains a synthetic method for annotations 79 * but such method is not found since it is synthetic and ignored by the processor. 80 */ isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement)81 public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) { 82 return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement); 83 } 84 85 /** Returns {@code true} if this type element is a Kotlin Object. */ isObjectClass(TypeElement typeElement)86 public boolean isObjectClass(TypeElement typeElement) { 87 return hasMetadata(typeElement) 88 && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT); 89 } 90 91 /** Returns {@code true} if this type element is a Kotlin data class. */ isDataClass(TypeElement typeElement)92 public boolean isDataClass(TypeElement typeElement) { 93 return hasMetadata(typeElement) 94 && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA); 95 } 96 97 /* Returns {@code true} if this type element is a Kotlin Companion Object. */ isCompanionObjectClass(TypeElement typeElement)98 public boolean isCompanionObjectClass(TypeElement typeElement) { 99 return hasMetadata(typeElement) 100 && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT); 101 } 102 103 /* Returns {@code true} if this type element has a Kotlin Companion Object. */ hasEnclosedCompanionObject(TypeElement typeElement)104 public boolean hasEnclosedCompanionObject(TypeElement typeElement) { 105 return hasMetadata(typeElement) 106 && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent(); 107 } 108 109 /* Returns the Companion Object element enclosed by the given type element. */ getEnclosedCompanionObject(TypeElement typeElement)110 public TypeElement getEnclosedCompanionObject(TypeElement typeElement) { 111 return metadataFactory 112 .create(typeElement) 113 .classMetadata() 114 .companionObjectName() 115 .map( 116 companionObjectName -> 117 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream() 118 .filter( 119 innerType -> innerType.getSimpleName().contentEquals(companionObjectName)) 120 .collect(DaggerCollectors.onlyElement())) 121 .get(); 122 } 123 124 /** 125 * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin 126 * source. 127 */ isVisibilityPrivate(TypeElement typeElement)128 public boolean isVisibilityPrivate(TypeElement typeElement) { 129 return hasMetadata(typeElement) 130 && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE); 131 } 132 133 /** 134 * Returns {@code true} if the given executable element was declared {@code internal} in its 135 * Kotlin source. 136 */ isVisibilityInternal(ExecutableElement method)137 public boolean isVisibilityInternal(ExecutableElement method) { 138 return hasMetadata(method) 139 && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL); 140 } 141 getPropertyGetter(VariableElement fieldElement)142 public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) { 143 return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement); 144 } 145 containsConstructorWithDefaultParam(TypeElement typeElement)146 public boolean containsConstructorWithDefaultParam(TypeElement typeElement) { 147 return hasMetadata(typeElement) 148 && metadataFactory.create(typeElement).containsConstructorWithDefaultParam(); 149 } 150 151 /** 152 * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element. 153 */ isJvmStaticPresent(ExecutableElement element)154 public static boolean isJvmStaticPresent(ExecutableElement element) { 155 return isAnnotationPresent(element, JvmStatic.class); 156 } 157 } 158