Mark argument types as live.

We assume that argument types would be seen and marked as live while
walking through live methods, as long as arguments are used. If some
argument is not used, an instance of any type can be flown, resulting in
missing argument types. Unlike JVM, which checks types when instances
are actually used, ART checks type compatibility of argument/parameter
when invocation target is identified, hence class not found error. It is
safer to mark the type of arguments as live when we handle live methods.

Bug: 112452064
Change-Id: I83c4c01740127b38214f7a59ce094fd316fa4d4c
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 27d3d3b..15ef2e6 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -911,8 +911,12 @@
     // Mark the type live here, so that the class exists at runtime. Note that this also marks all
     // supertypes as live, so even if the field is actually on a supertype, its class will be live.
     markTypeAsLive(field.clazz);
-    if (field.type.isClassType()) {
-      markTypeAsLive(field.type);
+    DexType fieldType = field.type;
+    if (fieldType.isArrayType()) {
+      fieldType = fieldType.toBaseType(appInfo.dexItemFactory);
+    }
+    if (fieldType.isClassType()) {
+      markTypeAsLive(fieldType);
     }
     // Find the actual field.
     DexEncodedField encodedField = appInfo.resolveFieldOn(field.clazz, field);
@@ -943,8 +947,12 @@
   private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
     assert field != null;
     markTypeAsLive(field.field.clazz);
-    if (field.field.type.isClassType()) {
-      markTypeAsLive(field.field.type);
+    DexType fieldType = field.field.type;
+    if (fieldType.isArrayType()) {
+      fieldType = fieldType.toBaseType(appInfo.dexItemFactory);
+    }
+    if (fieldType.isClassType()) {
+      markTypeAsLive(fieldType);
     }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
@@ -1376,6 +1384,14 @@
           markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method));
         }
       }
+      for (DexType parameterType : method.method.proto.parameters.values) {
+        if (parameterType.isArrayType()) {
+          parameterType = parameterType.toBaseType(appInfo.dexItemFactory);
+        }
+        if (parameterType.isClassType()) {
+          markTypeAsLive(parameterType);
+        }
+      }
       processAnnotations(method.annotations.annotations);
       method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
       if (protoLiteExtension != null && protoLiteExtension.appliesTo(method)) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 97042b8..03bb249 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -122,7 +122,6 @@
     assertThat(subInterface, isRenamed());
   }
 
-  @Ignore("b/112452064")
   @Test
   public void test_brokenTypeHierarchy_singleInterface() throws Exception {
     JasminBuilder jasminBuilder = new JasminBuilder();
@@ -199,7 +198,6 @@
     assertThat(subSubject, isPresent());
   }
 
-  @Ignore("b/112452064")
   @Test
   public void test_brokenTypeHierarchy_doubleInterfaces() throws Exception {
     JasminBuilder jasminBuilder = new JasminBuilder();
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index bc94fa6..092ae1d 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,10 +94,11 @@
 
       Result result = runTest(mainClass, proguardConfig);
 
-      // Without includedescriptorclasses return type and argument type are removed.
+      // Without includedescriptorclasses return type is removed.
       result.assertKept(ClassWithNativeMethods.class);
-      result.assertRemoved(NativeArgumentType.class);
       result.assertRemoved(NativeReturnType.class);
+      // Argument type is not removed due to the concern about the broken type hierarchy.
+      result.assertRenamed(NativeArgumentType.class);
       // Field type is not removed due to the concern about the broken type hierarchy.
       result.assertRenamed(InstanceFieldType.class);
       result.assertRenamed(StaticFieldType.class);