Don't skip partially migrated libraries.

More details in the bug.

Plus the scanner now also throws exception for malformed bytecode the
same way the ByteCodeTransformer so we warn the user properly.

Relnote: Partially migrated libraries are no longer skipped.

Bug: b/148462462
Test: Updated + added
Change-Id: I3502b2c87c04d8d3c6b16a554f1ea5190c39613d
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
index 2116d9e..9b20b6b 100644
--- a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
@@ -18,7 +18,7 @@
 
 object Log {
 
-    var currentLevel: LogLevel = LogLevel.ERROR
+    var currentLevel: LogLevel = LogLevel.WARNING
 
     var logConsumer: LogConsumer = StdOutLogConsumer()
 
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/AndroidXRefScanner.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/AndroidXRefScanner.kt
index 954a269d..daa6645 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/AndroidXRefScanner.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/AndroidXRefScanner.kt
@@ -16,9 +16,12 @@
 
 package com.android.tools.build.jetifier.processor
 
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.type.JavaType
 import com.android.tools.build.jetifier.processor.archive.Archive
 import com.android.tools.build.jetifier.processor.archive.ArchiveFile
 import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.bytecode.InvalidByteCodeException
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassWriter
 import org.objectweb.asm.commons.ClassRemapper
@@ -27,10 +30,28 @@
 /**
  * Scans java bytecode for any references to androidX.
  */
-class AndroidXRefScanner(private val library: Archive) : ArchiveItemVisitor {
+class AndroidXRefScanner(
+    private val library: Archive,
+    private val config: Config
+) : ArchiveItemVisitor {
 
     /** Whether any androidX references were discovered. Check after calling [scan]. */
-    var androidXDetected = false
+    val androidXDetected
+        get() = androidXRefExample != null
+    /** Whether any android support references were discovered. Check after calling [scan]. */
+    val androidSupportDetected
+        get() = androidSupportRefExample != null
+
+    /**
+     * Example of androidX reference that was discovered. This is null if no reference was found.
+     * Check after calling [scan].
+     */
+    var androidXRefExample: String? = null
+    /**
+     * Example of android support reference that was discovered. This is null if no reference was
+     * found. Check after calling [scan].
+     */
+    var androidSupportRefExample: String? = null
 
     fun scan(): AndroidXRefScanner {
         library.accept(this)
@@ -39,7 +60,7 @@
 
     override fun visit(archive: Archive) {
         archive.files.forEach {
-            if (androidXDetected) {
+            if (androidXDetected && androidSupportDetected) {
                 return@forEach
             }
 
@@ -55,22 +76,36 @@
         val reader = ClassReader(archiveFile.data)
         val writer = ClassWriter(0 /* flags */)
 
-        val androidXTrackingRemapper = AndroidXTrackingRemapper()
+        val androidXTrackingRemapper = AndroidXTrackingRemapper(config)
         val classRemapper = ClassRemapper(writer, androidXTrackingRemapper)
 
-        reader.accept(classRemapper, 0 /* flags */)
+        try {
+            reader.accept(classRemapper, 0 /* flags */)
+        } catch (e: ArrayIndexOutOfBoundsException) {
+            throw InvalidByteCodeException(
+                "Error processing '${archiveFile.relativePath}' bytecode.", e)
+        }
 
-        androidXDetected = androidXDetected || androidXTrackingRemapper.androidXDetected
+        if (androidXTrackingRemapper.androidXRefExample != null) {
+            androidXRefExample = androidXTrackingRemapper.androidXRefExample
+        }
+        if (androidXTrackingRemapper.androidSupportRefExample != null) {
+            androidSupportRefExample = androidXTrackingRemapper.androidSupportRefExample
+        }
     }
 
-    class AndroidXTrackingRemapper : Remapper() {
+    class AndroidXTrackingRemapper(private val config: Config) : Remapper() {
 
-        var androidXDetected = false
+        var androidXRefExample: String? = null
+        var androidSupportRefExample: String? = null
 
         override fun map(typeName: String): String {
             if (typeName.startsWith("androidx/")) {
-                androidXDetected = true
+                androidXRefExample = typeName
+            } else if (config.isEligibleForRewrite(JavaType(typeName))) {
+                androidSupportRefExample = typeName
             }
+
             return typeName
         }
     }
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
index e26feaf..e584260 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
@@ -376,8 +376,15 @@
 
         val newLibraries = mutableSetOf<Archive>()
         libraries.forEach {
-            val androidXScanner = AndroidXRefScanner(it).scan()
-            if (androidXScanner.androidXDetected) {
+            val androidXScanner = AndroidXRefScanner(it, context.config).scan()
+            if (androidXScanner.androidXDetected && androidXScanner.androidSupportDetected) {
+                Log.w(TAG, "Library '${it.relativePath}' contains references to both AndroidX and" +
+                        " old support library. This seems like the library is partially migrated." +
+                        " Jetifier will try to rewrite the library anyway.\n Example of androidX" +
+                        " reference: '${androidXScanner.androidXRefExample}'\n Example of" +
+                        " support library reference: '${androidXScanner.androidSupportRefExample}'")
+                newLibraries.add(it)
+            } else if (androidXScanner.androidXDetected) {
                 Log.i(TAG, "Library '${it.relativePath}' contains AndroidX reference and will be " +
                         "skipped.")
             } else {
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
index 641bc8c..7693d28 100644
--- a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
@@ -180,25 +180,7 @@
     }
 
     @Test
-    fun javaClass_androidXReferencesDetectionOn_archiveNotChanged() {
-        val inputFile =
-            File(javaClass.getResource("/changeDetectionTest/testPreference.class").file)
-        val inputFile2 =
-            File(javaClass.getResource("/classRewriteTest/ShareCompat.class").file)
-
-        testChange(
-            config = prefRewriteConfig,
-            files = listOf(
-                ArchiveFile(Paths.get("/", "preference.class"), inputFile.readBytes()),
-                ArchiveFile(Paths.get("/", "ShareCompat.class"), inputFile2.readBytes())
-            ),
-            areChangesExpected = false,
-            enableToSkipLibsWithAndroidXReferences = true
-        )
-    }
-
-    @Test
-    fun javaClass_androidXReferencesDetectionOff_archiveChanged() {
+    fun javaClass_referencesToBoth_androidXReferencesDetectionOn_archiveNotChanged() {
         val inputFile =
             File(javaClass.getResource("/changeDetectionTest/testPreference.class").file)
         val inputFile2 =
@@ -211,7 +193,37 @@
                 ArchiveFile(Paths.get("/", "ShareCompat.class"), inputFile2.readBytes())
             ),
             areChangesExpected = true,
-            enableToSkipLibsWithAndroidXReferences = false
+            enableToSkipLibsWithAndroidXReferences = true
+        )
+    }
+
+    @Test
+    fun javaClass_referencesToAndroidXOnly_androidXReferencesDetectionOn_archiveNotChanged() {
+        val inputFile =
+            File(javaClass.getResource("/classRewriteTest/ShareCompat.class").file)
+
+        testChange(
+            config = prefRewriteConfig,
+            files = listOf(
+                ArchiveFile(Paths.get("/", "ShareCompat.class"), inputFile.readBytes())
+            ),
+            areChangesExpected = false,
+            enableToSkipLibsWithAndroidXReferences = true
+        )
+    }
+
+    @Test
+    fun javaClass_referencesToAndroidXOnly_androidXReferencesDetectionOff_archiveChanged() {
+        val inputFile =
+            File(javaClass.getResource("/classRewriteTest/ShareCompat.class").file)
+
+        testChange(
+            config = prefRewriteConfig,
+            files = listOf(
+                ArchiveFile(Paths.get("/", "ShareCompat.class"), inputFile.readBytes())
+            ),
+            areChangesExpected = false,
+            enableToSkipLibsWithAndroidXReferences = true
         )
     }
 
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformerTest.kt
index 65ee514..9911c76 100644
--- a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformerTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformerTest.kt
@@ -36,4 +36,19 @@
             )
         )
     }
+
+    @Test(expected = InvalidByteCodeException::class)
+    fun malformedBytecode_androidXDetectionOn_shouldThrowException() {
+        val processor = Processor.createProcessor3(config = Config.EMPTY)
+        processor.transform2(
+            input = setOf(
+                FileMapping(
+                    File(javaClass
+                        .getResource("/malformedBytecodeTest/malformedBytecodeArchive.zip").file),
+                    File("test")
+                )
+            ),
+            skipLibsWithAndroidXReferences = true
+        )
+    }
 }
\ No newline at end of file