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 is a Kotlin object or companion object. */ isObjectOrCompanionObjectClass(TypeElement typeElement)104 public boolean isObjectOrCompanionObjectClass(TypeElement typeElement) { 105 return isObjectClass(typeElement) || isCompanionObjectClass(typeElement); 106 } 107 108 /* Returns {@code true} if this type element has a Kotlin Companion Object. */ hasEnclosedCompanionObject(TypeElement typeElement)109 public boolean hasEnclosedCompanionObject(TypeElement typeElement) { 110 return hasMetadata(typeElement) 111 && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent(); 112 } 113 114 /* Returns the Companion Object element enclosed by the given type element. */ getEnclosedCompanionObject(TypeElement typeElement)115 public TypeElement getEnclosedCompanionObject(TypeElement typeElement) { 116 return metadataFactory 117 .create(typeElement) 118 .classMetadata() 119 .companionObjectName() 120 .map( 121 companionObjectName -> 122 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream() 123 .filter( 124 innerType -> innerType.getSimpleName().contentEquals(companionObjectName)) 125 .collect(DaggerCollectors.onlyElement())) 126 .get(); 127 } 128 129 /** 130 * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin 131 * source. 132 */ isVisibilityPrivate(TypeElement typeElement)133 public boolean isVisibilityPrivate(TypeElement typeElement) { 134 return hasMetadata(typeElement) 135 && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE); 136 } 137 138 /** 139 * Returns {@code true} if the given type element was declared {@code internal} in its Kotlin 140 * source. 141 */ isVisibilityInternal(TypeElement type)142 public boolean isVisibilityInternal(TypeElement type) { 143 return hasMetadata(type) 144 && metadataFactory.create(type).classMetadata().flags(Flag.IS_INTERNAL); 145 } 146 147 /** 148 * Returns {@code true} if the given executable element was declared {@code internal} in its 149 * Kotlin source. 150 */ isVisibilityInternal(ExecutableElement method)151 public boolean isVisibilityInternal(ExecutableElement method) { 152 return hasMetadata(method) 153 && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL); 154 } 155 getPropertyGetter(VariableElement fieldElement)156 public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) { 157 return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement); 158 } 159 containsConstructorWithDefaultParam(TypeElement typeElement)160 public boolean containsConstructorWithDefaultParam(TypeElement typeElement) { 161 return hasMetadata(typeElement) 162 && metadataFactory.create(typeElement).containsConstructorWithDefaultParam(); 163 } 164 165 /** 166 * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element. 167 */ isJvmStaticPresent(ExecutableElement element)168 public static boolean isJvmStaticPresent(ExecutableElement element) { 169 return isAnnotationPresent(element, JvmStatic.class); 170 } 171 } 172