• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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