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 20 import static com.google.auto.common.MoreElements.isAnnotationPresent; 21 import static com.google.common.base.Preconditions.checkState; 22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 23 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement; 24 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations; 25 import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT; 26 import static kotlinx.metadata.Flag.Class.IS_DATA; 27 import static kotlinx.metadata.Flag.Class.IS_OBJECT; 28 import static kotlinx.metadata.Flag.IS_PRIVATE; 29 30 import com.google.common.collect.ImmutableCollection; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.ImmutableMap; 33 import com.squareup.javapoet.ClassName; 34 import dagger.internal.codegen.extension.DaggerCollectors; 35 import dagger.internal.codegen.kotlin.KotlinMetadata.FunctionMetadata; 36 import java.util.Optional; 37 import javax.inject.Inject; 38 import javax.lang.model.element.AnnotationMirror; 39 import javax.lang.model.element.Element; 40 import javax.lang.model.element.ExecutableElement; 41 import javax.lang.model.element.TypeElement; 42 import javax.lang.model.element.VariableElement; 43 import javax.lang.model.util.ElementFilter; 44 import kotlin.Metadata; 45 import kotlinx.metadata.Flag; 46 47 /** Utility class for interacting with Kotlin Metadata. */ 48 public final class KotlinMetadataUtil { 49 50 private final KotlinMetadataFactory metadataFactory; 51 52 @Inject KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)53 KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) { 54 this.metadataFactory = metadataFactory; 55 } 56 57 /** 58 * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in 59 * an element that does. 60 */ hasMetadata(Element element)61 public boolean hasMetadata(Element element) { 62 return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class); 63 } 64 65 /** 66 * Returns the synthetic annotations of a Kotlin property. 67 * 68 * <p>Note that this method only looks for additional annotations in the synthetic property 69 * method, if any, of a Kotlin property and not for annotations in its backing field. 70 */ getSyntheticPropertyAnnotations( VariableElement fieldElement, ClassName annotationType)71 public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations( 72 VariableElement fieldElement, ClassName annotationType) { 73 return metadataFactory 74 .create(fieldElement) 75 .getSyntheticAnnotationMethod(fieldElement) 76 .map(methodElement -> 77 getAnnotatedAnnotations(methodElement, annotationType).asList()) 78 .orElse(ImmutableList.of()); 79 } 80 81 /** 82 * Returns {@code true} if the synthetic method for annotations is missing. This can occur when 83 * the Kotlin metadata of the property reports that it contains a synthetic method for annotations 84 * but such method is not found since it is synthetic and ignored by the processor. 85 */ isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement)86 public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) { 87 return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement); 88 } 89 90 /** Returns {@code true} if this type element is a Kotlin Object. */ isObjectClass(TypeElement typeElement)91 public boolean isObjectClass(TypeElement typeElement) { 92 return hasMetadata(typeElement) 93 && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT); 94 } 95 96 /** Returns {@code true} if this type element is a Kotlin data class. */ isDataClass(TypeElement typeElement)97 public boolean isDataClass(TypeElement typeElement) { 98 return hasMetadata(typeElement) 99 && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA); 100 } 101 102 /* Returns {@code true} if this type element is a Kotlin Companion Object. */ isCompanionObjectClass(TypeElement typeElement)103 public boolean isCompanionObjectClass(TypeElement typeElement) { 104 return hasMetadata(typeElement) 105 && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT); 106 } 107 108 /** Returns {@code true} if this type element is a Kotlin object or companion object. */ isObjectOrCompanionObjectClass(TypeElement typeElement)109 public boolean isObjectOrCompanionObjectClass(TypeElement typeElement) { 110 return isObjectClass(typeElement) || isCompanionObjectClass(typeElement); 111 } 112 113 /* Returns {@code true} if this type element has a Kotlin Companion Object. */ hasEnclosedCompanionObject(TypeElement typeElement)114 public boolean hasEnclosedCompanionObject(TypeElement typeElement) { 115 return hasMetadata(typeElement) 116 && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent(); 117 } 118 119 /* Returns the Companion Object element enclosed by the given type element. */ getEnclosedCompanionObject(TypeElement typeElement)120 public TypeElement getEnclosedCompanionObject(TypeElement typeElement) { 121 return metadataFactory 122 .create(typeElement) 123 .classMetadata() 124 .companionObjectName() 125 .map( 126 companionObjectName -> 127 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream() 128 .filter( 129 innerType -> innerType.getSimpleName().contentEquals(companionObjectName)) 130 .collect(DaggerCollectors.onlyElement())) 131 .get(); 132 } 133 134 /** 135 * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin 136 * source. 137 */ isVisibilityPrivate(TypeElement typeElement)138 public boolean isVisibilityPrivate(TypeElement typeElement) { 139 return hasMetadata(typeElement) 140 && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE); 141 } 142 143 /** 144 * Returns {@code true} if the given type element was declared {@code internal} in its Kotlin 145 * source. 146 */ isVisibilityInternal(TypeElement type)147 public boolean isVisibilityInternal(TypeElement type) { 148 return hasMetadata(type) 149 && metadataFactory.create(type).classMetadata().flags(Flag.IS_INTERNAL); 150 } 151 152 /** 153 * Returns {@code true} if the given executable element was declared {@code internal} in its 154 * Kotlin source. 155 */ isVisibilityInternal(ExecutableElement method)156 public boolean isVisibilityInternal(ExecutableElement method) { 157 return hasMetadata(method) 158 && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL); 159 } 160 getPropertyGetter(VariableElement fieldElement)161 public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) { 162 return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement); 163 } 164 containsConstructorWithDefaultParam(TypeElement typeElement)165 public boolean containsConstructorWithDefaultParam(TypeElement typeElement) { 166 return hasMetadata(typeElement) 167 && metadataFactory.create(typeElement).containsConstructorWithDefaultParam(); 168 } 169 170 /** 171 * Returns a map mapping all method signatures within the given class element, including methods 172 * that it inherits from its ancestors, to their method names. 173 */ getAllMethodNamesBySignature(TypeElement element)174 public ImmutableMap<String, String> getAllMethodNamesBySignature(TypeElement element) { 175 checkState( 176 hasMetadata(element), "Can not call getAllMethodNamesBySignature for non-Kotlin class"); 177 return metadataFactory.create(element).classMetadata().functionsBySignature().values().stream() 178 .collect(toImmutableMap(FunctionMetadata::signature, FunctionMetadata::name)); 179 } 180 } 181