ASM throws ArrayIndexOutOfBoundsException when processing kotlin class file using both Inline Function and Multiplatform Project
Firebase Performance received a customer issue which is publicly tracked at https://github.com/firebase/firebase-android-sdk/issues/1556.
Note: When developer integrates Firebase Performance to their app, before application code is converted to DEX files, the Performance Monitoring plugin uses the Transform API and the ASM bytecode instrumentation framework to visit the app's compiled class files and to instrument the code (to measure performance). More details here.
The issue happens when developer integrates with Ktor (which is an asynchronous Web framework for Kotlin) and Firebase Performance.
I was able to reproduce the problem with this sample app from Ktor:
> Task :app:transformClassesWithFirebasePerformancePluginForDebug
java.lang.ClassNotFoundException: org.codehaus.groovy.control.CompilationFailedException
Can't instrument: io/ktor/client/request/forms/MultiPartFormDataContent.class
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(ArrayList.java:422)
at java.util.ArrayList.remove(ArrayList.java:499)
at org.objectweb.asm.commons.AdviceAdapter.popValue(AdviceAdapter.java:604)
at org.objectweb.asm.commons.AdviceAdapter.doVisitMethodInsn(AdviceAdapter.java:474)
at org.objectweb.asm.commons.AdviceAdapter.visitMethodInsn(AdviceAdapter.java:468)
at com.google.firebase.perf.plugin.instrumentation.InstrumentationVisitor$FirebasePerfMethodVisitor.visitMethodInsn(InstrumentationVisitor.java:358)
at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2209)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1275)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:679)
at com.google.firebase.perf.plugin.instrumentation.Instrument.instrument(Instrument.java:170)
at com.google.firebase.perf.plugin.instrumentation.Instrument.instrumentClassesInJar(Instrument.java:117)
at com.google.firebase.perf.plugin.FirebasePerfTransform.performTransformationFor(FirebasePerfTransform.java:563)
at com.google.firebase.perf.plugin.FirebasePerfTransform.transformJarInputs(FirebasePerfTransform.java:445)
at com.google.firebase.perf.plugin.FirebasePerfTransform.transform(FirebasePerfTransform.java:416)
at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:284)
at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:247)
at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:106)
at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:242)
After debugging further I figured out the factors that cause the issue are:
- Kotlin Multiplatform Project - Ktor uses this
- Kotlin Inline Functions - Ktor uses this
A minimal set to reproduce this problem is:
-
Create a Sample android app (you can use Fireperf Quickstart app)
-
Create a class like:
MultiplatformProjectInlineFunTest.kt
import android.app.Activity
import io.ktor.utils.io.core.BytePacketBuilder
class MultiplatformProjectInlineFunTest : Activity() {
val packet = buildPacket()
inline fun buildPacket(): BytePacketBuilder {
try {
return BytePacketBuilder(10)
} finally {
// this block is required
}
}
}
- Update your
project-level
build.gradle file:
buildscript {
repositories {
google()
jcenter()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70'
classpath 'com.google.firebase:perf-plugin:1.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
. . .
}
- Update your
app-level
build.gradle file:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.firebase.firebase-perf'
android { . . . }
dependencies {
. . .
implementation 'com.google.firebase:firebase-perf:19.0.7'
implementation "io.ktor:ktor-client-apache:1.3.2"
}
- Run the gradle
assemble
task and you should see the below error whentransformClassesWithFirebasePerformancePluginForDebug
task is executed:
> Task :app:transformClassesWithFirebasePerformancePluginForDebug
Can't instrument /Users/rkhinda/Documents/hackweek-q2-2020/SampleAndroidApp/app/build/tmp/kotlin-classes/debug/com/example/pressthebutton/MultiplatformProjectInlineFunTest.class : null
java.lang.ArrayIndexOutOfBoundsException
. . .