Skip to content

Commit f8f0dec

Browse files
eamonnmcmanusGoogle Java Core Libraries
authored and
Google Java Core Libraries
committed
Access the Kotlin metadata API through reflection.
This means we don't need a dependency on that API when building AutoValue. Therefore users can freely supply the version of the API that corresponds to the actual Kotlin version they are using. The downside is that they may need to add that dependency explicitly when previously they were getting it through AutoValue. (But possibly getting the wrong version.) Fixes #1574. RELNOTES=AutoValue no longer has an explicit dependency on the Kotlin Metadata API. **You may need to add an explicit dependency on `org.jetbrains.kotlinx:kotlinx-metadata-jvm.** The reason for this change is to avoid problems that occurred when the version of the API that AutoValue bundles was different from the version that the user code expected. See #1574. PiperOrigin-RevId: 556951184
1 parent b431b02 commit f8f0dec

File tree

4 files changed

+287
-101
lines changed

4 files changed

+287
-101
lines changed

value/processor/pom.xml

-5
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@
7878
<groupId>com.squareup</groupId>
7979
<artifactId>javapoet</artifactId>
8080
</dependency>
81-
<dependency>
82-
<groupId>org.jetbrains.kotlinx</groupId>
83-
<artifactId>kotlinx-metadata-jvm</artifactId>
84-
<version>0.7.0</version>
85-
</dependency>
8681
<dependency>
8782
<groupId>org.ow2.asm</groupId>
8883
<artifactId>asm</artifactId>

value/src/it/functional/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105
<artifactId>kotlin-stdlib</artifactId>
106106
<version>${kotlin.version}</version>
107107
</dependency>
108+
<dependency>
109+
<groupId>org.jetbrains.kotlinx</groupId>
110+
<artifactId>kotlinx-metadata-jvm</artifactId>
111+
<version>0.7.0</version>
112+
</dependency>
108113
</dependencies>
109114

110115
<build>

value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java

+3-96
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@
2121
import static com.google.auto.common.MoreStreams.toImmutableList;
2222
import static com.google.auto.common.MoreStreams.toImmutableMap;
2323
import static com.google.auto.common.MoreStreams.toImmutableSet;
24-
import static com.google.auto.common.MoreTypes.asTypeElement;
2524
import static com.google.auto.value.processor.AutoValueProcessor.OMIT_IDENTIFIERS_OPTION;
2625
import static com.google.auto.value.processor.ClassNames.AUTO_ANNOTATION_NAME;
2726
import static com.google.auto.value.processor.ClassNames.AUTO_BUILDER_NAME;
28-
import static com.google.auto.value.processor.ClassNames.KOTLIN_METADATA_NAME;
2927
import static java.util.stream.Collectors.joining;
3028
import static java.util.stream.Collectors.toMap;
3129
import static javax.lang.model.util.ElementFilter.constructorsIn;
@@ -48,7 +46,6 @@
4846
import java.lang.reflect.Field;
4947
import java.util.AbstractMap.SimpleEntry;
5048
import java.util.Collections;
51-
import java.util.LinkedHashMap;
5249
import java.util.List;
5350
import java.util.Map;
5451
import java.util.NavigableSet;
@@ -72,12 +69,6 @@
7269
import javax.lang.model.type.TypeKind;
7370
import javax.lang.model.type.TypeMirror;
7471
import javax.tools.JavaFileObject;
75-
import kotlinx.metadata.Flag;
76-
import kotlinx.metadata.KmClass;
77-
import kotlinx.metadata.KmConstructor;
78-
import kotlinx.metadata.KmValueParameter;
79-
import kotlinx.metadata.jvm.KotlinClassHeader;
80-
import kotlinx.metadata.jvm.KotlinClassMetadata;
8172
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
8273
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
8374

@@ -457,12 +448,13 @@ private Executable findExecutable(
457448

458449
private ImmutableList<Executable> findRelevantExecutables(
459450
TypeElement ofClass, String callMethod, TypeElement autoBuilderType) {
460-
Optional<AnnotationMirror> kotlinMetadata = kotlinMetadataAnnotation(ofClass);
451+
Optional<KotlinMetadata.KotlinMetadataAnnotation> kotlinMetadata =
452+
KotlinMetadata.kotlinMetadataAnnotation(ofClass);
461453
List<? extends Element> elements = ofClass.getEnclosedElements();
462454
Stream<Executable> relevantExecutables =
463455
callMethod.isEmpty()
464456
? kotlinMetadata
465-
.map(a -> kotlinConstructorsIn(a, ofClass).stream())
457+
.map(a -> KotlinMetadata.kotlinConstructorsIn(errorReporter(), a, ofClass).stream())
466458
.orElseGet(() -> constructorsIn(elements).stream().map(Executable::of))
467459
: methodsIn(elements).stream()
468460
.filter(m -> m.getSimpleName().contentEquals(callMethod))
@@ -575,91 +567,6 @@ private boolean visibleFrom(Element element, PackageElement fromPackage) {
575567
}
576568
}
577569

578-
private Optional<AnnotationMirror> kotlinMetadataAnnotation(Element element) {
579-
// It would be MUCH simpler if we could just use ofClass.getAnnotation(Metadata.class).
580-
// However that would be unsound. We want to shade the Kotlin runtime, including
581-
// kotlin.Metadata, so as not to interfere with other things on the annotation classpath that
582-
// might have a different version of the runtime. That means that if we referenced
583-
// kotlin.Metadata.class here we would actually be referencing
584-
// autovalue.shaded.kotlin.Metadata.class. Obviously the Kotlin class doesn't have that
585-
// annotation.
586-
return element.getAnnotationMirrors().stream()
587-
.filter(
588-
a ->
589-
asTypeElement(a.getAnnotationType())
590-
.getQualifiedName()
591-
.contentEquals(KOTLIN_METADATA_NAME))
592-
.<AnnotationMirror>map(a -> a) // get rid of that stupid wildcard
593-
.findFirst();
594-
}
595-
596-
/**
597-
* Use Kotlin reflection to build {@link Executable} instances for the constructors in {@code
598-
* ofClass} that include information about which parameters have default values.
599-
*/
600-
private ImmutableList<Executable> kotlinConstructorsIn(
601-
AnnotationMirror metadata, TypeElement ofClass) {
602-
ImmutableMap<String, AnnotationValue> annotationValues =
603-
AnnotationMirrors.getAnnotationValuesWithDefaults(metadata).entrySet().stream()
604-
.collect(toImmutableMap(e -> e.getKey().getSimpleName().toString(), e -> e.getValue()));
605-
// We match the KmConstructor instances with the ExecutableElement instances based on the
606-
// parameter names. We could possibly just assume that the constructors are in the same order.
607-
Map<ImmutableSet<String>, ExecutableElement> map =
608-
constructorsIn(ofClass.getEnclosedElements()).stream()
609-
.collect(toMap(c -> parameterNames(c), c -> c, (a, b) -> a, LinkedHashMap::new));
610-
ImmutableMap<ImmutableSet<String>, ExecutableElement> paramNamesToConstructor =
611-
ImmutableMap.copyOf(map);
612-
KotlinClassHeader header =
613-
new KotlinClassHeader(
614-
(Integer) annotationValues.get("k").getValue(),
615-
intArrayValue(annotationValues.get("mv")),
616-
stringArrayValue(annotationValues.get("d1")),
617-
stringArrayValue(annotationValues.get("d2")),
618-
(String) annotationValues.get("xs").getValue(),
619-
(String) annotationValues.get("pn").getValue(),
620-
(Integer) annotationValues.get("xi").getValue());
621-
KotlinClassMetadata.Class classMetadata =
622-
(KotlinClassMetadata.Class) KotlinClassMetadata.read(header);
623-
KmClass kmClass = classMetadata.toKmClass();
624-
ImmutableList.Builder<Executable> kotlinConstructorsBuilder = ImmutableList.builder();
625-
for (KmConstructor constructor : kmClass.getConstructors()) {
626-
ImmutableSet.Builder<String> allBuilder = ImmutableSet.builder();
627-
ImmutableSet.Builder<String> optionalBuilder = ImmutableSet.builder();
628-
for (KmValueParameter param : constructor.getValueParameters()) {
629-
String name = param.getName();
630-
allBuilder.add(name);
631-
if (Flag.ValueParameter.DECLARES_DEFAULT_VALUE.invoke(param.getFlags())) {
632-
optionalBuilder.add(name);
633-
}
634-
}
635-
ImmutableSet<String> optional = optionalBuilder.build();
636-
ImmutableSet<String> all = allBuilder.build();
637-
ExecutableElement javaConstructor = paramNamesToConstructor.get(all);
638-
if (javaConstructor != null) {
639-
kotlinConstructorsBuilder.add(Executable.of(javaConstructor, optional));
640-
}
641-
}
642-
return kotlinConstructorsBuilder.build();
643-
}
644-
645-
private static int[] intArrayValue(AnnotationValue value) {
646-
@SuppressWarnings("unchecked")
647-
List<AnnotationValue> list = (List<AnnotationValue>) value.getValue();
648-
return list.stream().mapToInt(v -> (int) v.getValue()).toArray();
649-
}
650-
651-
private static String[] stringArrayValue(AnnotationValue value) {
652-
@SuppressWarnings("unchecked")
653-
List<AnnotationValue> list = (List<AnnotationValue>) value.getValue();
654-
return list.stream().map(AnnotationValue::getValue).toArray(String[]::new);
655-
}
656-
657-
private static ImmutableSet<String> parameterNames(ExecutableElement executableElement) {
658-
return executableElement.getParameters().stream()
659-
.map(v -> v.getSimpleName().toString())
660-
.collect(toImmutableSet());
661-
}
662-
663570
private static final ElementKind ELEMENT_KIND_RECORD = elementKindRecord();
664571

665572
private static ElementKind elementKindRecord() {

0 commit comments

Comments
 (0)