Reland "Inspect kotlin metadata proto messages before modeling signatures"
Bug: 185756596
Change-Id: Ic2bbcd75db40a5f140bc3a25ffb6e042e359cf59
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index f10902f..c3bb323 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -84,13 +84,16 @@
}
public static KotlinClassInfo create(
- KmClass kmClass,
+ KotlinClassMetadata.Class metadata,
String packageName,
int[] metadataVersion,
DexClass hostClass,
DexItemFactory factory,
Reporter reporter,
Consumer<DexEncodedMethod> keepByteCode) {
+ KmClass kmClass = metadata.toKmClass();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(metadata);
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : hostClass.fields()) {
fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -100,9 +103,12 @@
methodMap.put(toJvmMethodSignature(method.getReference()).asString(), method);
}
ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
+ int constructorIndex = 0;
for (KmConstructor kmConstructor : kmClass.getConstructors()) {
+ boolean readConstructorSignature =
+ extensionInformation.hasJvmMethodSignatureExtensionForConstructor(constructorIndex++);
KotlinConstructorInfo constructorInfo =
- KotlinConstructorInfo.create(kmConstructor, factory, reporter);
+ KotlinConstructorInfo.create(kmConstructor, factory, reporter, readConstructorSignature);
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
if (signature != null) {
DexEncodedMethod method = methodMap.get(signature.asString());
@@ -116,7 +122,7 @@
}
KotlinDeclarationContainerInfo container =
KotlinDeclarationContainerInfo.create(
- kmClass, methodMap, fieldMap, factory, reporter, keepByteCode);
+ kmClass, methodMap, fieldMap, factory, reporter, keepByteCode, extensionInformation);
setCompanionObject(kmClass, hostClass, reporter);
return new KotlinClassInfo(
kmClass.getFlags(),
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 6fc09f5..cb0a7e8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -27,6 +27,9 @@
import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
public final class KotlinClassMetadataReader {
@@ -139,7 +142,7 @@
int[] metadataVersion = kMetadata.getHeader().getMetadataVersion();
if (kMetadata instanceof KotlinClassMetadata.Class) {
return KotlinClassInfo.create(
- ((KotlinClassMetadata.Class) kMetadata).toKmClass(),
+ (KotlinClassMetadata.Class) kMetadata,
packageName,
metadataVersion,
clazz,
@@ -149,7 +152,7 @@
} else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
// e.g., B.kt becomes class `BKt`
return KotlinFileFacadeInfo.create(
- (KotlinClassMetadata.FileFacade) kMetadata,
+ (FileFacade) kMetadata,
packageName,
metadataVersion,
clazz,
@@ -159,14 +162,11 @@
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
// multi-file class with the same @JvmName.
return KotlinMultiFileClassFacadeInfo.create(
- (KotlinClassMetadata.MultiFileClassFacade) kMetadata,
- packageName,
- metadataVersion,
- factory);
+ (MultiFileClassFacade) kMetadata, packageName, metadataVersion, factory);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
// A single file, which is part of multi-file class.
return KotlinMultiFileClassPartInfo.create(
- (KotlinClassMetadata.MultiFileClassPart) kMetadata,
+ (MultiFileClassPart) kMetadata,
packageName,
metadataVersion,
clazz,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index 2e4ee98..eb8421a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -41,12 +41,18 @@
}
public static KotlinConstructorInfo create(
- KmConstructor kmConstructor, DexItemFactory factory, Reporter reporter) {
+ KmConstructor kmConstructor,
+ DexItemFactory factory,
+ Reporter reporter,
+ boolean readConstructorSignature) {
return new KotlinConstructorInfo(
kmConstructor.getFlags(),
KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), factory, reporter),
KotlinVersionRequirementInfo.create(kmConstructor.getVersionRequirements()),
- KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), factory));
+ readConstructorSignature
+ ? KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getSignature(kmConstructor), factory)
+ : null);
}
public void rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 7447bbd..92db593 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -55,8 +55,10 @@
Map<String, DexEncodedField> fieldSignatureMap,
DexItemFactory factory,
Reporter reporter,
- Consumer<DexEncodedMethod> keepByteCode) {
+ Consumer<DexEncodedMethod> keepByteCode,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
+ int functionCounter = 0;
for (KmFunction kmFunction : container.getFunctions()) {
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
if (signature == null) {
@@ -64,7 +66,11 @@
continue;
}
KotlinFunctionInfo kotlinFunctionInfo =
- KotlinFunctionInfo.create(kmFunction, factory, reporter);
+ KotlinFunctionInfo.create(
+ kmFunction,
+ factory,
+ reporter,
+ extensionInformation.hasJvmMethodSignatureExtensionForFunction(functionCounter++));
DexEncodedMethod method = methodSignatureMap.get(signature.asString());
if (method == null) {
notBackedFunctions.add(kotlinFunctionInfo);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index b012758..abb6ff1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -39,9 +39,12 @@
DexItemFactory factory,
Reporter reporter,
Consumer<DexEncodedMethod> keepByteCode) {
+ KmPackage kmPackage = kmFileFacade.toKmPackage();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(kmFileFacade);
return new KotlinFileFacadeInfo(
KotlinPackageInfo.create(
- kmFileFacade.toKmPackage(), clazz, factory, reporter, keepByteCode),
+ kmPackage, clazz, factory, reporter, keepByteCode, extensionInformation),
packageName,
metadataVersion);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index 008177e..057f8bc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -73,7 +73,10 @@
}
static KotlinFunctionInfo create(
- KmFunction kmFunction, DexItemFactory factory, Reporter reporter) {
+ KmFunction kmFunction,
+ DexItemFactory factory,
+ Reporter reporter,
+ boolean readMethodSignature) {
boolean isCrossInline = false;
List<KotlinValueParameterInfo> valueParameters =
KotlinValueParameterInfo.create(kmFunction.getValueParameters(), factory, reporter);
@@ -90,7 +93,9 @@
KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), factory, reporter),
valueParameters,
KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), factory, reporter),
- KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory),
+ readMethodSignature
+ ? KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory)
+ : null,
getlambdaClassOrigin(kmFunction, factory),
KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
KotlinContractInfo.create(kmFunction.getContract(), factory, reporter),
@@ -106,6 +111,10 @@
return null;
}
+ public String getName() {
+ return name;
+ }
+
public void rewrite(
KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
DexEncodedMethod method,
@@ -162,10 +171,6 @@
return receiverParameterType != null;
}
- public KotlinJvmMethodSignatureInfo getSignature() {
- return signature;
- }
-
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
forEachApply(valueParameters, param -> param::trace, definitionSupplier);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index b0314be..400e66b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -50,25 +50,24 @@
if (methodSignature == null) {
return null;
}
- String kotlinDescriptor = methodSignature.getDesc();
- if (!KotlinMetadataUtils.isValidMethodDescriptor(kotlinDescriptor)) {
+ String name = methodSignature.getName();
+ String descriptor = methodSignature.getDesc();
+ if (!KotlinMetadataUtils.isValidMethodDescriptor(descriptor)) {
// If the method descriptor is invalid, keep it as invalid.
- return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), kotlinDescriptor);
+ return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), descriptor);
}
- String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(kotlinDescriptor);
+ String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(descriptor);
KotlinTypeReference returnType =
KotlinTypeReference.fromDescriptor(returnTypeDescriptor, factory);
- String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(kotlinDescriptor);
+ String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(descriptor);
if (descriptors.length == 0) {
- return new KotlinJvmMethodSignatureInfo(
- methodSignature.getName(), returnType, EMPTY_PARAMETERS_LIST);
+ return new KotlinJvmMethodSignatureInfo(name, returnType, EMPTY_PARAMETERS_LIST);
}
ImmutableList.Builder<KotlinTypeReference> parameters = ImmutableList.builder();
- for (String descriptor : descriptors) {
- parameters.add(KotlinTypeReference.fromDescriptor(descriptor, factory));
+ for (String paramDescriptor : descriptors) {
+ parameters.add(KotlinTypeReference.fromDescriptor(paramDescriptor, factory));
}
- return new KotlinJvmMethodSignatureInfo(
- methodSignature.getName(), returnType, parameters.build());
+ return new KotlinJvmMethodSignatureInfo(name, returnType, parameters.build());
}
public JvmMethodSignature rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
new file mode 100644
index 0000000..955c1ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ReflectionHelper;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import kotlin.Pair;
+import kotlinx.metadata.internal.metadata.ProtoBuf;
+import kotlinx.metadata.internal.metadata.jvm.JvmProtoBuf;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
+
+// Due to kotlin-metadata-jvm library synthesizing jvm method signatures from type-information
+// we do an extra check to figure out if we should model the signature. If we model it, we will
+// add name and descriptor information to the string pool for the proto message, so it is
+// better to avoid it.
+// https://github.com/Kotlin/kotlinx.reflect.lite/blob/46d47f118f9846166b6b8f8212bdbc822fe2f634/src/main/java/org/jetbrains/kotlin/serialization/jvm/JvmProtoBufUtil.kt
+
+public class KotlinJvmSignatureExtensionInformation {
+
+ private final Set<Integer> noExtensionIndicesForFunctions;
+ private final Set<Integer> noExtensionIndicesForConstructors;
+
+ private static final KotlinJvmSignatureExtensionInformation EMPTY = builder().build();
+
+ private KotlinJvmSignatureExtensionInformation(
+ Set<Integer> noExtensionIndicesForFunctions, Set<Integer> noExtensionIndicesForConstructors) {
+ this.noExtensionIndicesForFunctions = noExtensionIndicesForFunctions;
+ this.noExtensionIndicesForConstructors = noExtensionIndicesForConstructors;
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ FileFacade fileFacadeMetadata) {
+ return readPackageDataFromMessage(fileFacadeMetadata);
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ MultiFileClassPart classPart) {
+ return readPackageDataFromMessage(classPart);
+ }
+
+ private static KotlinJvmSignatureExtensionInformation readPackageDataFromMessage(Object object) {
+ try {
+ Pair<?, ProtoBuf.Package> kotlinPairData =
+ ReflectionHelper.performReflection(
+ object,
+ ReflectionHelper.builder()
+ .readField("packageData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ return builder().visit(kotlinPairData.getSecond()).build();
+ } catch (Exception e) {
+ return empty();
+ }
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ SyntheticClass syntheticClass) {
+ try {
+ Pair<?, ProtoBuf.Function> kotlinPairData =
+ ReflectionHelper.performReflection(
+ syntheticClass,
+ ReflectionHelper.builder()
+ .readField("functionData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ if (kotlinPairData == null) {
+ return empty();
+ }
+ return builder().visit(kotlinPairData.getSecond(), 0).build();
+ } catch (Exception e) {
+ return empty();
+ }
+ }
+
+ public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+ KotlinClassMetadata.Class kMetadata) {
+ try {
+ Pair<?, ProtoBuf.Class> kotlinPairData =
+ ReflectionHelper.performReflection(
+ kMetadata,
+ ReflectionHelper.builder()
+ .readField("classData$delegate")
+ .setSetAccessible(true)
+ .done()
+ .readMethod("getValue")
+ .setSetAccessible(true)
+ .done()
+ .build());
+ return builder().visit(kotlinPairData.getSecond()).build();
+ } catch (Exception e) {
+ return empty();
+ }
+ }
+
+ public boolean hasJvmMethodSignatureExtensionForFunction(int index) {
+ return !noExtensionIndicesForFunctions.contains(index);
+ }
+
+ public boolean hasJvmMethodSignatureExtensionForConstructor(int index) {
+ return !noExtensionIndicesForConstructors.contains(index);
+ }
+
+ public static KotlinJvmSignatureExtensionInformationBuilder builder() {
+ return new KotlinJvmSignatureExtensionInformationBuilder();
+ }
+
+ public static KotlinJvmSignatureExtensionInformation empty() {
+ return EMPTY;
+ }
+
+ private static class KotlinJvmSignatureExtensionInformationBuilder {
+
+ private final Set<Integer> noExtensionIndicesForFunctions = new HashSet<>();
+ private final Set<Integer> noExtensionIndicesForConstructors = new HashSet<>();
+
+ private KotlinJvmSignatureExtensionInformation build() {
+ return new KotlinJvmSignatureExtensionInformation(
+ noExtensionIndicesForFunctions, noExtensionIndicesForConstructors);
+ }
+
+ private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Class clazz) {
+ visitFunctions(clazz.getFunctionList());
+ visitConstructors(clazz.getConstructorList());
+ return this;
+ }
+
+ private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Package pkg) {
+ visitFunctions(pkg.getFunctionList());
+ return this;
+ }
+
+ private void visitFunctions(List<ProtoBuf.Function> functions) {
+ ListUtils.forEachWithIndex(functions, this::visit);
+ }
+
+ public KotlinJvmSignatureExtensionInformationBuilder visit(
+ ProtoBuf.Function function, int index) {
+ if (!function.hasExtension(JvmProtoBuf.methodSignature)) {
+ noExtensionIndicesForFunctions.add(index);
+ }
+ return this;
+ }
+
+ private void visitConstructors(List<ProtoBuf.Constructor> constructors) {
+ ListUtils.forEachWithIndex(
+ constructors,
+ (constructor, index) -> {
+ if (!constructor.hasExtension(JvmProtoBuf.constructorSignature)) {
+ noExtensionIndicesForConstructors.add(index);
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 01a7c2a..df28ccf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -30,13 +30,21 @@
}
static KotlinLambdaInfo create(
- DexClass clazz, KmLambda lambda, DexItemFactory factory, Reporter reporter) {
+ DexClass clazz,
+ KmLambda lambda,
+ DexItemFactory factory,
+ Reporter reporter,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
if (lambda == null) {
assert false;
return null;
}
KotlinFunctionInfo kotlinFunctionInfo =
- KotlinFunctionInfo.create(lambda.function, factory, reporter);
+ KotlinFunctionInfo.create(
+ lambda.function,
+ factory,
+ reporter,
+ extensionInformation.hasJvmMethodSignatureExtensionForFunction(0));
JvmMethodSignature signature = JvmExtensionsKt.getSignature(lambda.function);
if (signature != null) {
for (DexEncodedMethod method : clazz.methods()) {
@@ -69,8 +77,7 @@
appView
.options()
.reporter
- .info(
- KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getSignature()));
+ .info(KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getName()));
return false;
}
function.rewrite(visitorProvider.get()::visitFunction, backing, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
index 3415e13..6c4c963 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
@@ -74,13 +74,12 @@
+ StringUtils.stacktraceAsString(t));
}
- static KotlinMetadataDiagnostic lambdaBackingNotFound(
- DexType type, KotlinJvmMethodSignatureInfo signatureInfo) {
+ static KotlinMetadataDiagnostic lambdaBackingNotFound(DexType type, String functionName) {
return new KotlinMetadataDiagnostic(
Origin.unknown(),
Position.UNKNOWN,
"The lambda function "
- + signatureInfo.toString()
+ + functionName
+ " could no longer be found in "
+ type.toSourceString()
+ " . The method is most likely pruned and would require a specific keep rule to keep"
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 4eef6cb..d092bff 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -84,7 +84,6 @@
}
}
});
- appView.setCfByteCodePassThrough(keepByteCodeFunctions);
for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
EnclosingMethodAttribute enclosingAttribute =
localOrAnonymousClass.getEnclosingMethodAttribute();
@@ -103,6 +102,7 @@
m -> keepByteCodeFunctions.add(m.getReference()));
}
}
+ appView.setCfByteCodePassThrough(keepByteCodeFunctions);
} else {
assert verifyKotlinMetadataModeledForAllClasses(enqueuer, keepMetadata);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 4a5904e..ee582bb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -101,11 +101,11 @@
appView.appInfo().classes(),
clazz -> {
KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
- DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
if (kotlinInfo == INVALID_KOTLIN_INFO) {
// Maintain invalid kotlin info for classes.
return;
}
+ DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
// TODO(b/181103083): Consider removing if rewrittenMetadataType
// != factory.kotlinMetadataType
if (oldMeta == null
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 6b99d15..e0db449 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -45,9 +45,13 @@
DexItemFactory factory,
Reporter reporter,
Consumer<DexEncodedMethod> keepByteCode) {
+ KmPackage kmPackage = classPart.toKmPackage();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(classPart);
return new KotlinMultiFileClassPartInfo(
classPart.getFacadeClassName(),
- KotlinPackageInfo.create(classPart.toKmPackage(), clazz, factory, reporter, keepByteCode),
+ KotlinPackageInfo.create(
+ kmPackage, clazz, factory, reporter, keepByteCode, extensionInformation),
packageName,
metadataVersion);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 41ff17d..397d0fa 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -44,7 +44,8 @@
DexClass clazz,
DexItemFactory factory,
Reporter reporter,
- Consumer<DexEncodedMethod> keepByteCode) {
+ Consumer<DexEncodedMethod> keepByteCode,
+ KotlinJvmSignatureExtensionInformation extensionInformation) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : clazz.fields()) {
fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -56,7 +57,7 @@
return new KotlinPackageInfo(
JvmExtensionsKt.getModuleName(kmPackage),
KotlinDeclarationContainerInfo.create(
- kmPackage, methodMap, fieldMap, factory, reporter, keepByteCode),
+ kmPackage, methodMap, fieldMap, factory, reporter, keepByteCode, extensionInformation),
KotlinLocalDelegatedPropertyInfo.create(
JvmExtensionsKt.getLocalDelegatedProperties(kmPackage), factory, reporter));
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 36bf5cf..b2381de 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -47,13 +47,14 @@
Kotlin kotlin,
DexItemFactory factory,
Reporter reporter) {
- KmLambda lambda = null;
- if (syntheticClass.isLambda()) {
- lambda = syntheticClass.toKmLambda();
- assert lambda != null;
- }
+ KmLambda lambda = syntheticClass.toKmLambda();
+ assert lambda == null || syntheticClass.isLambda();
+ KotlinJvmSignatureExtensionInformation extensionInformation =
+ KotlinJvmSignatureExtensionInformation.readInformationFromMessage(syntheticClass);
return new KotlinSyntheticClassInfo(
- lambda != null ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter) : null,
+ lambda != null
+ ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter, extensionInformation)
+ : null,
getFlavour(syntheticClass, clazz, kotlin),
packageName,
metadataVersion);
diff --git a/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
new file mode 100644
index 0000000..5e1cdbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class ReflectionHelper {
+
+ @SuppressWarnings("unchecked")
+ public static <T> T performReflection(Object object, ReflectiveOperation<?> operation)
+ throws Exception {
+ return (T) operation.compute(object);
+ }
+
+ public static ReflectiveOperationSequenceBuilder builder() {
+ return new ReflectiveOperationSequenceBuilder();
+ }
+
+ public enum DeclaredType {
+ FIELD,
+ METHOD
+ }
+
+ public abstract static class ReflectiveOperation<Member> {
+
+ final Class<?> classForDeclaration;
+ final String declaredMember;
+ final Consumer<Member> modifier;
+ final ReflectiveOperation<?> nextOperation;
+
+ private ReflectiveOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Member> modifier) {
+ this.classForDeclaration = classForDeclaration;
+ this.declaredMember = declaredMember;
+ this.nextOperation = nextOperation;
+ this.modifier = modifier;
+ }
+
+ public abstract Object compute(Object object) throws Exception;
+ }
+
+ public static class ReflectiveMethodOperation extends ReflectiveOperation<Method> {
+
+ private ReflectiveMethodOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Method> modifier) {
+ super(classForDeclaration, declaredMember, nextOperation, modifier);
+ }
+
+ @Override
+ public Object compute(Object object) throws Exception {
+ Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+ Method declaredMethod = clazz.getDeclaredMethod(declaredMember);
+ modifier.accept(declaredMethod);
+ // The reflection helper do not support arguments at this point.
+ Object returnValue = declaredMethod.invoke(object);
+ return nextOperation != null ? nextOperation.compute(returnValue) : returnValue;
+ }
+ }
+
+ public static class ReflectiveFieldOperation extends ReflectiveOperation<Field> {
+
+ private ReflectiveFieldOperation(
+ Class<?> classForDeclaration,
+ String declaredMember,
+ ReflectiveOperation<?> nextOperation,
+ Consumer<Field> modifier) {
+ super(classForDeclaration, declaredMember, nextOperation, modifier);
+ }
+
+ @Override
+ public Object compute(Object object) throws Exception {
+ Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+ Field declaredField = clazz.getDeclaredField(declaredMember);
+ modifier.accept(declaredField);
+ Object fieldValue = declaredField.get(object);
+ return nextOperation != null ? nextOperation.compute(fieldValue) : fieldValue;
+ }
+ }
+
+ public static class ReflectiveOperationSequenceBuilder {
+
+ List<ReflectiveOperationBuilder> reflectiveOperationBuilderList = new ArrayList<>();
+
+ public ReflectiveOperationBuilder readMethod(String declaredMember) {
+ return add(declaredMember, DeclaredType.METHOD);
+ }
+
+ public ReflectiveOperationBuilder readField(String declaredMember) {
+ return add(declaredMember, DeclaredType.FIELD);
+ }
+
+ private ReflectiveOperationBuilder add(String declaredMember, DeclaredType declaredType) {
+ ReflectiveOperationBuilder reflectiveOperationBuilder =
+ new ReflectiveOperationBuilder(declaredMember, declaredType, this);
+ reflectiveOperationBuilderList.add(reflectiveOperationBuilder);
+ return reflectiveOperationBuilder;
+ }
+
+ public ReflectiveOperation<?> build() {
+ assert !reflectiveOperationBuilderList.isEmpty();
+ ReflectiveOperation<?> lastOperation = null;
+ for (int i = reflectiveOperationBuilderList.size() - 1; i >= 0; i--) {
+ lastOperation = reflectiveOperationBuilderList.get(i).build(lastOperation);
+ }
+ return lastOperation;
+ }
+ }
+
+ public static class ReflectiveOperationBuilder {
+
+ private final String declaredMember;
+ private final DeclaredType declaredType;
+ private boolean setAccessible = false;
+ private final ReflectiveOperationSequenceBuilder sequenceBuilder;
+
+ private ReflectiveOperationBuilder(
+ String declaredMember,
+ DeclaredType declaredType,
+ ReflectiveOperationSequenceBuilder sequenceBuilder) {
+ this.declaredMember = declaredMember;
+ this.declaredType = declaredType;
+ this.sequenceBuilder = sequenceBuilder;
+ }
+
+ public ReflectiveOperationBuilder setSetAccessible(boolean setAccessible) {
+ this.setAccessible = setAccessible;
+ return this;
+ }
+
+ public ReflectiveOperationSequenceBuilder done() {
+ return sequenceBuilder;
+ }
+
+ private ReflectiveOperation<?> build(ReflectiveOperation<?> nextOperation) {
+ if (declaredType == DeclaredType.FIELD) {
+ return new ReflectiveFieldOperation(
+ null,
+ declaredMember,
+ nextOperation,
+ field -> {
+ if (setAccessible) {
+ field.setAccessible(true);
+ }
+ });
+ } else {
+ assert declaredType == DeclaredType.METHOD;
+ return new ReflectiveMethodOperation(
+ null,
+ declaredMember,
+ nextOperation,
+ method -> {
+ if (setAccessible) {
+ method.setAccessible(true);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index f3e6dd4..e03ef61 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -29,3 +29,20 @@
# TODO(b/176783536): Avoid need to use -dontwarn.
-include dontwarn.txt
+
+# TODO(b/185756596): Remove when no longer needed
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$FileFacade {
+ com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$Class {
+ com.android.tools.r8.jetbrains.kotlin.Lazy classData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$SyntheticClass {
+ com.android.tools.r8.jetbrains.kotlin.Lazy functionData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$MultiFileClassPart {
+ com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlin.SafePublicationLazyImpl {
+ java.lang.Object getValue();
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 7f59069..11ad201 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -94,7 +94,10 @@
.compile()
.inspect(
inspector ->
- assertEqualMetadata(new CodeInspector(BASE_LIBRARY), inspector, i -> {}))
+ assertEqualMetadata(
+ new CodeInspector(BASE_LIBRARY),
+ inspector,
+ (addedStrings, addedNonInitStrings) -> {}))
.writeToZip();
Path testJar = compileTestSources(baseJar);
runTestsInJar(testJar, baseJar);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index 8178717..996dc79 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import com.android.tools.r8.KotlinTestBase;
@@ -19,9 +21,15 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
-import java.util.function.IntConsumer;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
import junit.framework.TestCase;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -35,7 +43,6 @@
static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
- static final String KT_ANY = "Lkotlin/Any;";
static final String KT_ARRAY = "Lkotlin/Array;";
static final String KT_CHAR_SEQUENCE = "Lkotlin/CharSequence;";
static final String KT_STRING = "Lkotlin/String;";
@@ -50,9 +57,13 @@
public void assertEqualMetadata(
CodeInspector originalInspector,
CodeInspector rewrittenInspector,
- IntConsumer addedStringsInspector) {
+ BiConsumer<Integer, Integer> addedStringsInspector) {
IntBox addedStrings = new IntBox();
- for (FoundClassSubject clazzSubject : originalInspector.allClasses()) {
+ IntBox addedNonInitStrings = new IntBox();
+ for (FoundClassSubject clazzSubject :
+ originalInspector.allClasses().stream()
+ .sorted(Comparator.comparing(FoundClassSubject::getFinalName))
+ .collect(Collectors.toList())) {
ClassSubject r8Clazz = rewrittenInspector.clazz(clazzSubject.getOriginalName());
assertThat(r8Clazz, isPresent());
KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
@@ -65,9 +76,17 @@
KotlinClassHeader originalHeader = originalMetadata.getHeader();
KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
- // TODO(b/154199572): Should we check for meta-data version?
- TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+ // We cannot assert equality of the data since it may be ordered differently. However, we
+ // will check for the changes to the string pool and then validate the same parsing
+ // by using the KotlinMetadataWriter.
+ Map<String, List<String>> descriptorToNames = new HashMap<>();
+ clazzSubject.forAllMethods(
+ method ->
+ descriptorToNames
+ .computeIfAbsent(
+ method.getFinalSignature().toDescriptor(), ignoreArgument(ArrayList::new))
+ .add(method.getFinalName()));
HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.getData2()));
HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.getData2()));
rewrittenStrings.forEach(
@@ -76,14 +95,28 @@
return;
}
addedStrings.increment();
+ // The init is not needed by if we cannot lookup the descriptor in the table, we have
+ // to emit it and that adds <init>.
+ if (rewrittenString.equals("<init>")) {
+ return;
+ }
+ // We have decided to keep invalid signatures, but they will end up in the string pool
+ // when we emit them. The likely cause of them not being there in the first place seems
+ // to be that they are not correctly written in the type table.
+ if (rewrittenString.equals("L;") || rewrittenString.equals("(L;)V")) {
+ return;
+ }
+ System.out.println(clazzSubject.toString() + ": " + rewrittenString);
+ addedNonInitStrings.increment();
});
- // We cannot assert equality of the data since it may be ordered differently. Instead we use
- // the KotlinMetadataWriter.
+ System.out.flush();
+ assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+
String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
- TestCase.assertEquals(expected, actual);
+ assertEquals(expected, actual);
}
- addedStringsInspector.accept(addedStrings.get());
+ addedStringsInspector.accept(addedStrings.get(), addedNonInitStrings.get());
}
public static void verifyExpectedWarningsFromKotlinReflectAndStdLib(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 12c1934..ffe8b3a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -77,7 +77,7 @@
assertEqualMetadata(
new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
inspector,
- i -> {}))
+ (addedStrings, addedNonInitStrings) -> {}))
.writeToZip();
testForJvm()
.addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index 4da59e8..39b540f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -40,11 +40,23 @@
public int getExpectedAddedCount() {
if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
- return 2441;
+ return 597;
} else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
- return 2561;
+ return 685;
} else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
- return 2594;
+ return 694;
+ } else {
+ throw new Unreachable("Should not compile in this configuration");
+ }
+ }
+
+ public int getExpectedNonInitAddedCount() {
+ if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+ return 327;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+ return 413;
+ } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+ return 417;
} else {
throw new Unreachable("Should not compile in this configuration");
}
@@ -66,8 +78,9 @@
assertEqualMetadata(
new CodeInspector(getKotlinStdlibJar(kotlinc)),
inspector,
- addedStrings -> {
- assertEquals(getExpectedAddedCount(), addedStrings);
+ (addedStrings, addedNonInitStrings) -> {
+ assertEquals(getExpectedAddedCount(), addedStrings.intValue());
+ assertEquals(getExpectedNonInitAddedCount(), addedNonInitStrings.intValue());
}));
}
}