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,