Towards synthesizing Kotlin @Metadata: constructors.
Bug: 70169921
Change-Id: I3a8fa9ee1ac58defe4233f6011607b690e160ebd
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 65fb712..77545740 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -29,6 +29,7 @@
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
@@ -213,6 +214,21 @@
return Arrays.asList(virtualMethods);
}
+ public Map<DexEncodedMethod, KmConstructor> kotlinConstructors(
+ List<KmConstructor> constructors, AppView<?> appView) {
+ ImmutableMap.Builder<DexEncodedMethod, KmConstructor> builder = ImmutableMap.builder();
+ for (DexEncodedMethod method : directMethods) {
+ if (method.isInstanceInitializer()) {
+ KmConstructor constructor = method.findCompatibleKotlinConstructor(constructors, appView);
+ if (constructor != null) {
+ // Found a compatible constructor that is likely asked to keep.
+ builder.put(method, constructor);
+ }
+ }
+ }
+ return builder.build();
+ }
+
public Map<DexEncodedMethod, KmFunction> kotlinExtensions(
List<KmFunction> extensions, AppView<?> appView) {
ImmutableMap.Builder<DexEncodedMethod, KmFunction> builder = ImmutableMap.builder();
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 90fee5d..dbd455b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -74,6 +74,7 @@
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.IntPredicate;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import org.objectweb.asm.Opcodes;
@@ -352,6 +353,20 @@
}
// TODO(b/70169921): Handling JVM extensions as well.
+ KmConstructor findCompatibleKotlinConstructor(
+ List<KmConstructor> constructors, AppView<?> appView) {
+ if (!isInstanceInitializer()) {
+ return null;
+ }
+ for (KmConstructor constructor : constructors) {
+ if (KotlinMetadataSynthesizer.isCompatibleConstructor(constructor, this, appView)) {
+ return constructor;
+ }
+ }
+ return null;
+ }
+
+ // TODO(b/70169921): Handling JVM extensions as well.
KmFunction findCompatibleKotlinExtension(List<KmFunction> extensions, AppView<?> appView) {
if (!isStaticMember()) {
return null;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 2ac086e..06db71b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmConstructor;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
@@ -17,10 +18,12 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
@@ -69,6 +72,18 @@
superTypes.add(toKmType(addKotlinPrefix("Any;")));
}
+ List<KmConstructor> constructors = kmClass.getConstructors();
+ List<KmConstructor> originalConstructors = new ArrayList<>(constructors);
+ constructors.clear();
+ for (Map.Entry<DexEncodedMethod, KmConstructor> entry :
+ clazz.kotlinConstructors(originalConstructors, appView).entrySet()) {
+ KmConstructor constructor =
+ toRenamedKmConstructor(entry.getKey(), entry.getValue(), appView, lens);
+ if (constructor != null) {
+ constructors.add(constructor);
+ }
+ }
+
List<KmFunction> functions = kmClass.getFunctions();
List<KmFunction> originalFunctions =
functions.stream()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index 1620eac..ff9ad15 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -16,7 +16,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Box;
import java.util.List;
+import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
@@ -83,6 +85,21 @@
return descriptor.equals(getDescriptorFromKmType(kmType));
}
+ public static boolean isCompatibleConstructor(
+ KmConstructor constructor, DexEncodedMethod method, AppView<?> appView) {
+ List<KmValueParameter> parameters = constructor.getValueParameters();
+ if (method.method.proto.parameters.size() != parameters.size()) {
+ return false;
+ }
+ for (int i = 0; i < method.method.proto.parameters.size(); i++) {
+ KmType kmType = parameters.get(i).getType();
+ if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public static boolean isCompatibleFunction(
KmFunction function, DexEncodedMethod method, AppView<?> appView) {
if (!function.getName().equals(method.method.name.toString())) {
@@ -132,6 +149,30 @@
return true;
}
+ static KmConstructor toRenamedKmConstructor(
+ DexEncodedMethod method,
+ KmConstructor original,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens) {
+ // Make sure it is an instance initializer and live.
+ if (!method.isInstanceInitializer()
+ || !appView.appInfo().liveMethods.contains(method.method)) {
+ return null;
+ }
+ // TODO(b/70169921): {@link KmConstructor.extensions} is private, i.e., no way to alter!
+ // Thus, we rely on original metadata for now.
+ Box<Boolean> hasJvmExtension = new Box<>(false);
+ KmConstructor kmConstructor =
+ hasJvmExtension.get()
+ ? original
+ // TODO(b/70169921): Consult kotlinx.metadata.Flag.Constructor to set IS_PRIMARY.
+ : new KmConstructor(method.accessFlags.getAsKotlinFlags());
+ List<KmValueParameter> parameters = kmConstructor.getValueParameters();
+ parameters.clear();
+ populateKmValueParameters(parameters, method, appView, lens, false);
+ return kmConstructor;
+ }
+
static KmFunction toRenamedKmFunction(
DexEncodedMethod method,
KmFunction original,