Repository: autonomousapps/dependency-analysis-android-gradle-plugin
Issue: Class scanning fails with java.lang.ArrayIndexOutOfBoundsException


[dependency-analysis-shortened.log](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/files/4989991/dependency-analysis-shortened.log)
[dependency-graph.txt](https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/files/4989994/dependency-graph.txt)

Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 32800 out of bounds for length 15
  at com.autonomousapps.internal.asm.ClassReader.readUtf(ClassReader.java:3641)

Last line of debug log:
 [DEBUG] [org.gradle.api.Task] ClassNameCollector#visit: PACKAGE_PRIVATE org/objectweb/asm/all/module-info extends null


Changed files:
src/main/kotlin/com/autonomousapps/tasks/FindUnusedProcsTask.kt, src/main/kotlin/com/autonomousapps/tasks/classAnalysisTasks.kt, src/main/kotlin/com/autonomousapps/internal/classReferenceParsers.kt, src/main/kotlin/com/autonomousapps/internal/utils/collections.kt, src/main/kotlin/com/autonomousapps/internal/JarAnalyzer.kt, src/main/kotlin/com/autonomousapps/tasks/InlineMemberExtractionTask.kt

diff --git a/src/main/kotlin/com/autonomousapps/internal/JarAnalyzer.kt b/src/main/kotlin/com/autonomousapps/internal/JarAnalyzer.kt
index a91ea464..4d1edb70 100644
--- a/src/main/kotlin/com/autonomousapps/internal/JarAnalyzer.kt
+++ b/src/main/kotlin/com/autonomousapps/internal/JarAnalyzer.kt
@@ -2,6 +2,7 @@ package com.autonomousapps.internal
 
 import com.autonomousapps.advice.Dependency
 import com.autonomousapps.internal.asm.ClassReader
+import com.autonomousapps.internal.utils.asClassFiles
 import com.autonomousapps.internal.utils.mapToOrderedSet
 import com.autonomousapps.internal.utils.mapToSet
 import com.autonomousapps.services.InMemoryCache
@@ -64,8 +65,7 @@ internal class JarAnalyzer(
     inMemoryCache.updateJars(zip.name)
 
     val ktFiles = KtFile.fromZip(zip)
-    val analyzedClasses = zip.entries().toList()
-      .filter { it.name.endsWith(".class") }
+    val analyzedClasses = zip.asClassFiles()
       .map { classEntry ->
         ClassNameAndAnnotationsVisitor(logger).apply {
           val reader = zip.getInputStream(classEntry).use { ClassReader(it.readBytes()) }
diff --git a/src/main/kotlin/com/autonomousapps/internal/classReferenceParsers.kt b/src/main/kotlin/com/autonomousapps/internal/classReferenceParsers.kt
index 7b26f57b..d6ac29ac 100644
--- a/src/main/kotlin/com/autonomousapps/internal/classReferenceParsers.kt
+++ b/src/main/kotlin/com/autonomousapps/internal/classReferenceParsers.kt
@@ -2,15 +2,7 @@ package com.autonomousapps.internal
 
 import com.autonomousapps.advice.VariantFile
 import com.autonomousapps.internal.asm.ClassReader
-import com.autonomousapps.internal.utils.JAVA_FQCN_REGEX
-import com.autonomousapps.internal.utils.JAVA_FQCN_REGEX_SLASHY
-import com.autonomousapps.internal.utils.buildDocument
-import com.autonomousapps.internal.utils.filterToSet
-import com.autonomousapps.internal.utils.flatMapToSet
-import com.autonomousapps.internal.utils.getLogger
-import com.autonomousapps.internal.utils.map
-import com.autonomousapps.internal.utils.mapToOrderedSet
-import com.autonomousapps.internal.utils.mapToSet
+import com.autonomousapps.internal.utils.*
 import org.gradle.api.logging.Logger
 import java.io.File
 import java.util.zip.ZipFile
@@ -134,8 +126,7 @@ internal class JarReader(
   private val zipFile = ZipFile(jarFile)
 
   override fun parseBytecode(): List<VariantClass> {
-    return zipFile.entries().toList()
-      .filterToSet { it.name.endsWith(".class") }
+    return zipFile.asClassFiles()
       .map { classEntry ->
         val variants = variantsFromPath(classEntry.name)
         val usedClasses = zipFile.getInputStream(classEntry).use { BytecodeParser(it.readBytes(), logger).parse() }
diff --git a/src/main/kotlin/com/autonomousapps/internal/utils/collections.kt b/src/main/kotlin/com/autonomousapps/internal/utils/collections.kt
index 32365041..3adfada7 100644
--- a/src/main/kotlin/com/autonomousapps/internal/utils/collections.kt
+++ b/src/main/kotlin/com/autonomousapps/internal/utils/collections.kt
@@ -1,11 +1,41 @@
 package com.autonomousapps.internal.utils
 
+import org.gradle.api.file.FileCollection
 import org.w3c.dom.Node
 import org.w3c.dom.NodeList
 import java.util.*
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
 import kotlin.collections.HashSet
 import kotlin.collections.LinkedHashSet
 
+/**
+ * Transforms a [ZipFile] into a collection of [ZipEntry]s, which contains only class files (and not
+ * the module-info.class file).
+ */
+internal fun ZipFile.asClassFiles(): Set<ZipEntry> {
+  return entries().toList().filterToSetOfClassFiles()
+}
+
+/**
+ * Filters a collection of [ZipEntry]s to contain only class files (and not the module-info.class
+ * file).
+ */
+internal fun Iterable<ZipEntry>.filterToSetOfClassFiles(): Set<ZipEntry> {
+  return filterToSet {
+    it.name.endsWith(".class") && it.name != "module-info.class"
+  }
+}
+
+/**
+ * Filters a [FileCollection] to contain only class files (and not the module-info.class file).
+ */
+internal fun FileCollection.filterToClassFiles(): FileCollection {
+  return filter {
+    it.isFile && it.name.endsWith(".class") && it.name != "module-info.class"
+  }
+}
+
 internal inline fun <T> Iterable<T>.filterToSet(predicate: (T) -> Boolean): Set<T> {
   return filterTo(HashSet(), predicate)
 }
diff --git a/src/main/kotlin/com/autonomousapps/tasks/FindUnusedProcsTask.kt b/src/main/kotlin/com/autonomousapps/tasks/FindUnusedProcsTask.kt
index e1b9281a..a92093b4 100644
--- a/src/main/kotlin/com/autonomousapps/tasks/FindUnusedProcsTask.kt
+++ b/src/main/kotlin/com/autonomousapps/tasks/FindUnusedProcsTask.kt
@@ -88,7 +88,7 @@ abstract class FindUnusedProcsTask : DefaultTask() {
 
   private fun findUsedProcsInClassFiles(classFiles: FileCollection): Set<AnnotationProcessor> {
     return classFiles
-      .filter { it.name.endsWith(".class") }
+      .filterToClassFiles()
       .flatMapToSet { classFile ->
         val visitor = ProcClassVisitor(logger, annotationProcessors)
         val reader = classFile.inputStream().use { ClassReader(it.readBytes()) }
@@ -100,8 +100,7 @@ abstract class FindUnusedProcsTask : DefaultTask() {
   private fun findUsedProcsInJar(jarFile: File): Set<AnnotationProcessor> {
     val zip = ZipFile(jarFile)
 
-    return zip.entries().toList()
-      .filter { it.name.endsWith(".class") }
+    return zip.asClassFiles()
       .flatMapToSet { classEntry ->
         val visitor = ProcClassVisitor(logger, annotationProcessors)
         val reader = zip.getInputStream(classEntry).use { ClassReader(it.readBytes()) }
diff --git a/src/main/kotlin/com/autonomousapps/tasks/InlineMemberExtractionTask.kt b/src/main/kotlin/com/autonomousapps/tasks/InlineMemberExtractionTask.kt
index 438f54e1..6fce6a07 100644
--- a/src/main/kotlin/com/autonomousapps/tasks/InlineMemberExtractionTask.kt
+++ b/src/main/kotlin/com/autonomousapps/tasks/InlineMemberExtractionTask.kt
@@ -205,8 +205,7 @@ internal class InlineMemberFinder(
       return emptyList()
     }
 
-    return entries
-      .filter { it.name.endsWith(".class") }
+    return entries.filterToSetOfClassFiles()
       .flatMap { entry ->
         // TODO an entry with `META-INF/proguard/androidx-annotations.pro`
         val classReader = zipFile.getInputStream(entry).use { ClassReader(it.readBytes()) }
diff --git a/src/main/kotlin/com/autonomousapps/tasks/classAnalysisTasks.kt b/src/main/kotlin/com/autonomousapps/tasks/classAnalysisTasks.kt
index 5ae81c7a..7da188b2 100644
--- a/src/main/kotlin/com/autonomousapps/tasks/classAnalysisTasks.kt
+++ b/src/main/kotlin/com/autonomousapps/tasks/classAnalysisTasks.kt
@@ -181,7 +181,7 @@ abstract class ClassListAnalysisTask @Inject constructor(
     val reportPrettyFile = outputPretty.getAndDelete()
 
     val inputClassFiles = javaClasses.asFileTree.plus(kotlinClasses)
-      .filter { it.isFile && it.name.endsWith(".class") }
+      .filterToClassFiles()
       .files
 
     logger.log("Java class files:${javaClasses.joinToString(prefix = "\\n- ", separator = "\\n- ") { it.path }}")