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 dagger.internal.codegen.base.MoreAnnotationValues.getIntArrayValue; 20 import static dagger.internal.codegen.base.MoreAnnotationValues.getIntValue; 21 import static dagger.internal.codegen.base.MoreAnnotationValues.getOptionalIntValue; 22 import static dagger.internal.codegen.base.MoreAnnotationValues.getOptionalStringValue; 23 import static dagger.internal.codegen.base.MoreAnnotationValues.getStringArrayValue; 24 import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue; 25 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 26 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; 27 import static dagger.internal.codegen.langmodel.DaggerElements.getFieldDescriptor; 28 import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor; 29 import static kotlinx.metadata.Flag.ValueParameter.DECLARES_DEFAULT_VALUE; 30 31 import com.google.auto.value.AutoValue; 32 import com.google.auto.value.extension.memoized.Memoized; 33 import com.google.common.base.Preconditions; 34 import com.google.common.collect.ImmutableList; 35 import com.google.common.collect.ImmutableMap; 36 import com.google.common.collect.ImmutableSet; 37 import dagger.internal.codegen.extension.DaggerCollectors; 38 import dagger.internal.codegen.langmodel.DaggerElements; 39 import java.util.HashMap; 40 import java.util.Map; 41 import java.util.Optional; 42 import java.util.function.Function; 43 import javax.annotation.Nullable; 44 import javax.lang.model.element.AnnotationMirror; 45 import javax.lang.model.element.ExecutableElement; 46 import javax.lang.model.element.TypeElement; 47 import javax.lang.model.element.VariableElement; 48 import javax.lang.model.util.ElementFilter; 49 import kotlin.Metadata; 50 import kotlinx.metadata.Flag; 51 import kotlinx.metadata.KmClassVisitor; 52 import kotlinx.metadata.KmConstructorExtensionVisitor; 53 import kotlinx.metadata.KmConstructorVisitor; 54 import kotlinx.metadata.KmExtensionType; 55 import kotlinx.metadata.KmFunctionExtensionVisitor; 56 import kotlinx.metadata.KmFunctionVisitor; 57 import kotlinx.metadata.KmPropertyExtensionVisitor; 58 import kotlinx.metadata.KmPropertyVisitor; 59 import kotlinx.metadata.KmValueParameterVisitor; 60 import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor; 61 import kotlinx.metadata.jvm.JvmFieldSignature; 62 import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor; 63 import kotlinx.metadata.jvm.JvmMethodSignature; 64 import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor; 65 import kotlinx.metadata.jvm.KotlinClassHeader; 66 import kotlinx.metadata.jvm.KotlinClassMetadata; 67 68 /** Data class of a TypeElement and its Kotlin metadata. */ 69 @AutoValue 70 abstract class KotlinMetadata { 71 // Kotlin suffix for fields that are for a delegated property. 72 // See: 73 // https://github.com/JetBrains/kotlin/blob/master/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt#L32 74 private static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate"; 75 76 // Map that associates field elements with its Kotlin synthetic method for annotations. 77 private final Map<VariableElement, Optional<MethodForAnnotations>> 78 elementFieldAnnotationMethodMap = new HashMap<>(); 79 80 // Map that associates field elements with its Kotlin getter method. 81 private final Map<VariableElement, Optional<ExecutableElement>> elementFieldGetterMethodMap = 82 new HashMap<>(); 83 typeElement()84 abstract TypeElement typeElement(); 85 classMetadata()86 abstract ClassMetadata classMetadata(); 87 88 @Memoized methodDescriptors()89 ImmutableMap<String, ExecutableElement> methodDescriptors() { 90 return ElementFilter.methodsIn(typeElement().getEnclosedElements()).stream() 91 .collect(toImmutableMap(DaggerElements::getMethodDescriptor, Function.identity())); 92 } 93 94 /** Returns true if any constructor of the defined a default parameter. */ 95 @Memoized containsConstructorWithDefaultParam()96 boolean containsConstructorWithDefaultParam() { 97 return classMetadata().constructors().stream() 98 .flatMap(constructor -> constructor.parameters().stream()) 99 .anyMatch(parameter -> parameter.flags(DECLARES_DEFAULT_VALUE)); 100 } 101 102 /** Gets the synthetic method for annotations of a given field element. */ getSyntheticAnnotationMethod(VariableElement fieldElement)103 Optional<ExecutableElement> getSyntheticAnnotationMethod(VariableElement fieldElement) { 104 return getAnnotationMethod(fieldElement) 105 .map( 106 methodForAnnotations -> { 107 if (methodForAnnotations == MethodForAnnotations.MISSING) { 108 throw new IllegalStateException( 109 "Method for annotations is missing for " + fieldElement); 110 } 111 return methodForAnnotations.method(); 112 }); 113 } 114 115 /** 116 * Returns true if the synthetic method for annotations is missing. This can occur when inspecting 117 * the Kotlin metadata of a property from another compilation unit. 118 */ 119 boolean isMissingSyntheticAnnotationMethod(VariableElement fieldElement) { 120 return getAnnotationMethod(fieldElement) 121 .map(methodForAnnotations -> methodForAnnotations == MethodForAnnotations.MISSING) 122 // This can be missing if there was no property annotation at all (e.g. no annotations or 123 // the qualifier is already properly attached to the field). For these cases, it isn't 124 // considered missing since there was no method to look for in the first place. 125 .orElse(false); 126 } 127 128 private Optional<MethodForAnnotations> getAnnotationMethod(VariableElement fieldElement) { 129 return elementFieldAnnotationMethodMap.computeIfAbsent( 130 fieldElement, this::getAnnotationMethodUncached); 131 } 132 133 private Optional<MethodForAnnotations> getAnnotationMethodUncached(VariableElement fieldElement) { 134 return findProperty(fieldElement) 135 .methodForAnnotationsSignature() 136 .map( 137 signature -> 138 Optional.ofNullable(methodDescriptors().get(signature)) 139 .map(MethodForAnnotations::create) 140 // The method may be missing across different compilations. 141 // See https://youtrack.jetbrains.com/issue/KT-34684 142 .orElse(MethodForAnnotations.MISSING)); 143 } 144 145 /** Gets the getter method of a given field element corresponding to a property. */ 146 Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) { 147 return elementFieldGetterMethodMap.computeIfAbsent( 148 fieldElement, this::getPropertyGetterUncached); 149 } 150 151 private Optional<ExecutableElement> getPropertyGetterUncached(VariableElement fieldElement) { 152 return findProperty(fieldElement) 153 .getterSignature() 154 .flatMap(signature -> Optional.ofNullable(methodDescriptors().get(signature))); 155 } 156 157 private PropertyMetadata findProperty(VariableElement field) { 158 String fieldDescriptor = getFieldDescriptor(field); 159 if (classMetadata().propertiesByFieldSignature().containsKey(fieldDescriptor)) { 160 return classMetadata().propertiesByFieldSignature().get(fieldDescriptor); 161 } else { 162 // Fallback to finding property by name, see: https://youtrack.jetbrains.com/issue/KT-35124 163 final String propertyName = getPropertyNameFromField(field); 164 return classMetadata().propertiesByFieldSignature().values().stream() 165 .filter(property -> propertyName.contentEquals(property.name())) 166 .collect(DaggerCollectors.onlyElement()); 167 } 168 } 169 170 private static String getPropertyNameFromField(VariableElement field) { 171 String name = field.getSimpleName().toString(); 172 if (name.endsWith(DELEGATED_PROPERTY_NAME_SUFFIX)) { 173 return name.substring(0, name.length() - DELEGATED_PROPERTY_NAME_SUFFIX.length()); 174 } else { 175 return name; 176 } 177 } 178 179 FunctionMetadata getFunctionMetadata(ExecutableElement method) { 180 return classMetadata().functionsBySignature().get(getMethodDescriptor(method)); 181 } 182 183 /** Parse Kotlin class metadata from a given type element * */ 184 static KotlinMetadata from(TypeElement typeElement) { 185 return new AutoValue_KotlinMetadata( 186 typeElement, ClassVisitor.createClassMetadata(metadataOf(typeElement))); 187 } 188 189 private static KotlinClassMetadata.Class metadataOf(TypeElement typeElement) { 190 Optional<AnnotationMirror> metadataAnnotation = 191 getAnnotationMirror(typeElement, Metadata.class); 192 Preconditions.checkState(metadataAnnotation.isPresent()); 193 KotlinClassHeader header = 194 new KotlinClassHeader( 195 getIntValue(metadataAnnotation.get(), "k"), 196 getIntArrayValue(metadataAnnotation.get(), "mv"), 197 getIntArrayValue(metadataAnnotation.get(), "bv"), 198 getStringArrayValue(metadataAnnotation.get(), "d1"), 199 getStringArrayValue(metadataAnnotation.get(), "d2"), 200 getStringValue(metadataAnnotation.get(), "xs"), 201 getOptionalStringValue(metadataAnnotation.get(), "pn").orElse(null), 202 getOptionalIntValue(metadataAnnotation.get(), "xi").orElse(null)); 203 KotlinClassMetadata metadata = KotlinClassMetadata.read(header); 204 if (metadata == null) { 205 // Should only happen on Kotlin < 1.0 (i.e. metadata version < 1.1) 206 throw new IllegalStateException( 207 "Unsupported metadata version. Check that your Kotlin version is >= 1.0"); 208 } 209 if (metadata instanceof KotlinClassMetadata.Class) { 210 // TODO(danysantiago): If when we need other types of metadata then move to right method. 211 return (KotlinClassMetadata.Class) metadata; 212 } else { 213 throw new IllegalStateException("Unsupported metadata type: " + metadata); 214 } 215 } 216 217 private static final class ClassVisitor extends KmClassVisitor { 218 static ClassMetadata createClassMetadata(KotlinClassMetadata.Class data) { 219 ClassVisitor visitor = new ClassVisitor(); 220 data.accept(visitor); 221 return visitor.classMetadata.build(); 222 } 223 224 private final ClassMetadata.Builder classMetadata = ClassMetadata.builder(); 225 226 @Override 227 public void visit(int flags, String name) { 228 classMetadata.flags(flags).name(name); 229 } 230 231 @Override 232 public KmConstructorVisitor visitConstructor(int flags) { 233 return new KmConstructorVisitor() { 234 private final FunctionMetadata.Builder constructor = 235 FunctionMetadata.builder(flags, "<init>"); 236 237 @Override 238 public KmValueParameterVisitor visitValueParameter(int flags, String name) { 239 constructor.addParameter(ValueParameterMetadata.create(flags, name)); 240 return super.visitValueParameter(flags, name); 241 } 242 243 @Override 244 public KmConstructorExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) { 245 return kmExtensionType.equals(JvmConstructorExtensionVisitor.TYPE) 246 ? new JvmConstructorExtensionVisitor() { 247 @Override 248 public void visit(JvmMethodSignature jvmMethodSignature) { 249 constructor.signature(jvmMethodSignature.asString()); 250 } 251 } 252 : null; 253 } 254 255 @Override 256 public void visitEnd() { 257 classMetadata.addConstructor(constructor.build()); 258 } 259 }; 260 } 261 262 @Override 263 public KmFunctionVisitor visitFunction(int flags, String name) { 264 return new KmFunctionVisitor() { 265 private final FunctionMetadata.Builder function = FunctionMetadata.builder(flags, name); 266 267 @Override 268 public KmValueParameterVisitor visitValueParameter(int flags, String name) { 269 function.addParameter(ValueParameterMetadata.create(flags, name)); 270 return super.visitValueParameter(flags, name); 271 } 272 273 @Override 274 public KmFunctionExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) { 275 return kmExtensionType.equals(JvmFunctionExtensionVisitor.TYPE) 276 ? new JvmFunctionExtensionVisitor() { 277 @Override 278 public void visit(JvmMethodSignature jvmMethodSignature) { 279 function.signature(jvmMethodSignature.asString()); 280 } 281 } 282 : null; 283 } 284 285 @Override 286 public void visitEnd() { 287 classMetadata.addFunction(function.build()); 288 } 289 }; 290 } 291 292 @Override 293 public void visitCompanionObject(String companionObjectName) { 294 classMetadata.companionObjectName(companionObjectName); 295 } 296 297 @Override 298 public KmPropertyVisitor visitProperty( 299 int flags, String name, int getterFlags, int setterFlags) { 300 return new KmPropertyVisitor() { 301 private final PropertyMetadata.Builder property = PropertyMetadata.builder(flags, name); 302 303 @Override 304 public KmPropertyExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) { 305 if (!kmExtensionType.equals(JvmPropertyExtensionVisitor.TYPE)) { 306 return null; 307 } 308 309 return new JvmPropertyExtensionVisitor() { 310 @Override 311 public void visit( 312 int jvmFlags, 313 @Nullable JvmFieldSignature jvmFieldSignature, 314 @Nullable JvmMethodSignature jvmGetterSignature, 315 @Nullable JvmMethodSignature jvmSetterSignature) { 316 property.fieldSignature( 317 Optional.ofNullable(jvmFieldSignature).map(JvmFieldSignature::asString)); 318 property.getterSignature( 319 Optional.ofNullable(jvmGetterSignature).map(JvmMethodSignature::asString)); 320 } 321 322 @Override 323 public void visitSyntheticMethodForAnnotations( 324 @Nullable JvmMethodSignature methodSignature) { 325 property.methodForAnnotationsSignature( 326 Optional.ofNullable(methodSignature).map(JvmMethodSignature::asString)); 327 } 328 }; 329 } 330 331 @Override 332 public void visitEnd() { 333 classMetadata.addProperty(property.build()); 334 } 335 }; 336 } 337 } 338 339 @AutoValue 340 abstract static class ClassMetadata extends BaseMetadata { 341 abstract Optional<String> companionObjectName(); 342 343 abstract ImmutableSet<FunctionMetadata> constructors(); 344 345 abstract ImmutableMap<String, FunctionMetadata> functionsBySignature(); 346 347 abstract ImmutableMap<String, PropertyMetadata> propertiesByFieldSignature(); 348 349 static Builder builder() { 350 return new AutoValue_KotlinMetadata_ClassMetadata.Builder(); 351 } 352 353 @AutoValue.Builder 354 abstract static class Builder implements BaseMetadata.Builder<Builder> { 355 abstract Builder companionObjectName(String companionObjectName); 356 357 abstract ImmutableSet.Builder<FunctionMetadata> constructorsBuilder(); 358 359 abstract ImmutableMap.Builder<String, FunctionMetadata> functionsBySignatureBuilder(); 360 361 abstract ImmutableMap.Builder<String, PropertyMetadata> propertiesByFieldSignatureBuilder(); 362 363 Builder addConstructor(FunctionMetadata constructor) { 364 constructorsBuilder().add(constructor); 365 return this; 366 } 367 368 Builder addFunction(FunctionMetadata function) { 369 functionsBySignatureBuilder().put(function.signature(), function); 370 return this; 371 } 372 373 Builder addProperty(PropertyMetadata property) { 374 if (property.fieldSignature().isPresent()) { 375 propertiesByFieldSignatureBuilder().put(property.fieldSignature().get(), property); 376 } 377 return this; 378 } 379 380 abstract ClassMetadata build(); 381 } 382 } 383 384 @AutoValue 385 abstract static class FunctionMetadata extends BaseMetadata { 386 abstract String signature(); 387 388 abstract ImmutableList<ValueParameterMetadata> parameters(); 389 390 static Builder builder(int flags, String name) { 391 return new AutoValue_KotlinMetadata_FunctionMetadata.Builder().flags(flags).name(name); 392 } 393 394 @AutoValue.Builder 395 abstract static class Builder implements BaseMetadata.Builder<Builder> { 396 abstract Builder signature(String signature); 397 398 abstract ImmutableList.Builder<ValueParameterMetadata> parametersBuilder(); 399 400 Builder addParameter(ValueParameterMetadata parameter) { 401 parametersBuilder().add(parameter); 402 return this; 403 } 404 405 abstract FunctionMetadata build(); 406 } 407 } 408 409 @AutoValue 410 abstract static class PropertyMetadata extends BaseMetadata { 411 /** Returns the JVM field descriptor of the backing field of this property. */ 412 abstract Optional<String> fieldSignature(); 413 414 abstract Optional<String> getterSignature(); 415 416 /** Returns JVM method descriptor of the synthetic method for property annotations. */ 417 abstract Optional<String> methodForAnnotationsSignature(); 418 419 static Builder builder(int flags, String name) { 420 return new AutoValue_KotlinMetadata_PropertyMetadata.Builder().flags(flags).name(name); 421 } 422 423 @AutoValue.Builder 424 interface Builder extends BaseMetadata.Builder<Builder> { 425 Builder fieldSignature(Optional<String> signature); 426 427 Builder getterSignature(Optional<String> signature); 428 429 Builder methodForAnnotationsSignature(Optional<String> signature); 430 431 PropertyMetadata build(); 432 } 433 } 434 435 @AutoValue 436 abstract static class ValueParameterMetadata extends BaseMetadata { 437 private static ValueParameterMetadata create(int flags, String name) { 438 return new AutoValue_KotlinMetadata_ValueParameterMetadata(flags, name); 439 } 440 } 441 442 abstract static class BaseMetadata { 443 /** Returns the Kotlin metadata flags for this property. */ 444 abstract int flags(); 445 446 /** returns {@code true} if the given flag (e.g. {@link Flag.IS_PRIVATE}) applies. */ 447 boolean flags(Flag flag) { 448 return flag.invoke(flags()); 449 } 450 451 /** Returns the simple name of this property. */ 452 abstract String name(); 453 454 interface Builder<BuilderT> { 455 BuilderT flags(int flags); 456 457 BuilderT name(String name); 458 } 459 } 460 461 @AutoValue 462 abstract static class MethodForAnnotations { 463 static MethodForAnnotations create(ExecutableElement method) { 464 return new AutoValue_KotlinMetadata_MethodForAnnotations(method); 465 } 466 467 static final MethodForAnnotations MISSING = MethodForAnnotations.create(null); 468 469 @Nullable 470 abstract ExecutableElement method(); 471 } 472 } 473