All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sentry.android.gradle.instrumentation.androidx.room.AndroidXRoomDao.kt Maven / Gradle / Ivy

There is a newer version: 4.11.0
Show newest version
@file:Suppress("UnstableApiUsage")

package io.sentry.android.gradle.instrumentation.androidx.room

import com.android.build.api.instrumentation.ClassContext
import io.sentry.android.gradle.SentryPlugin
import io.sentry.android.gradle.instrumentation.ClassInstrumentable
import io.sentry.android.gradle.instrumentation.CommonClassVisitor
import io.sentry.android.gradle.instrumentation.MethodContext
import io.sentry.android.gradle.instrumentation.MethodInstrumentable
import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory
import io.sentry.android.gradle.instrumentation.androidx.room.visitor.AndroidXRoomDaoVisitor
import io.sentry.android.gradle.instrumentation.androidx.room.visitor.InstrumentableMethodsCollectingVisitor
import io.sentry.android.gradle.util.info
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.MethodNode

class AndroidXRoomDao : ClassInstrumentable {

    override val fqName: String get() = "androidx.room.Dao"

    override fun getVisitor(
        instrumentableContext: ClassContext,
        apiVersion: Int,
        originalVisitor: ClassVisitor,
        parameters: SpanAddingClassVisitorFactory.SpanAddingParameters
    ): ClassVisitor {
        val currentClassName = instrumentableContext.currentClassData.className
        val originalClassName = currentClassName.substringBefore(IMPL_SUFFIX)
        val originalClass = instrumentableContext.loadClassData(originalClassName)
        return if (originalClass != null && fqName in originalClass.classAnnotations) {
            InstrumentableMethodsCollectingVisitor(
                apiVersion,
                nextVisitorInitializer = { methodsToInstrument ->
                    if (methodsToInstrument.isEmpty()) {
                        originalVisitor
                    } else {
                        CommonClassVisitor(
                            apiVersion = apiVersion,
                            classVisitor = originalVisitor,
                            className = currentClassName.substringAfterLast('.'),
                            methodInstrumentables = methodsToInstrument.map { (methodNode, type) ->
                                RoomMethod(originalClassName, methodNode, type)
                            },
                            parameters = parameters
                        )
                    }
                }
            )
        } else {
            if (parameters.debug.get() && originalClass == null) {
                SentryPlugin.logger.info {
                    "Expected $originalClassName in the classpath, but failed to discover"
                }
            }
            originalVisitor
        }
    }

    override fun isInstrumentable(data: ClassContext): Boolean =
        // this is not very deterministic, but we filter out false positives in [getVisitor]
        // based on the androidx.room.Dao annotation
        IMPL_SUFFIX in data.currentClassData.className

    companion object {
        private const val IMPL_SUFFIX = "_Impl"
    }
}

class RoomMethod(
    private val className: String,
    private val methodNode: MethodNode,
    private val type: RoomMethodType
) : MethodInstrumentable {

    override val fqName: String = methodNode.name

    override fun getVisitor(
        instrumentableContext: MethodContext,
        apiVersion: Int,
        originalVisitor: MethodVisitor,
        parameters: SpanAddingClassVisitorFactory.SpanAddingParameters
    ): MethodVisitor = AndroidXRoomDaoVisitor(
        className,
        apiVersion,
        originalVisitor,
        instrumentableContext.access,
        instrumentableContext.descriptor,
        type
    )

    override fun isInstrumentable(data: MethodContext): Boolean {
        return data.name == fqName && data.descriptor == methodNode.desc &&
            // we only care about public Dao methods
            (data.access and Opcodes.ACC_PUBLIC) != 0 &&
            // there's a synthetic bridge method generated by java compiler with the same name which we don't want to instrument
            (data.access and Opcodes.ACC_BRIDGE) == 0
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy