feat: Retrieve annotations in super and interface classes

This commit is contained in:
oSumAtrIX 2023-11-29 01:14:46 +01:00
parent f1de9b39ef
commit 7aeae93f3d
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
5 changed files with 106 additions and 29 deletions

View file

@ -1,35 +1,61 @@
@file:Suppress("UNCHECKED_CAST")
package app.revanced.patcher.extensions
import kotlin.reflect.KClass
internal object AnnotationExtensions {
/**
* Search for an annotation recursively.
*
* @param targetAnnotationClass The annotation class to search for.
* @param searchedClasses A set of annotations that have already been searched.
* @return The annotation if found, otherwise null.
*/
fun <T : Annotation> Class<*>.findAnnotationRecursively(
targetAnnotationClass: Class<T>,
searchedClasses: HashSet<Annotation> = hashSetOf(),
): T? {
annotations.forEach { annotation ->
// Terminate if the annotation is already searched.
if (annotation in searchedClasses) return@forEach
searchedClasses.add(annotation)
// Terminate if the annotation is found.
if (targetAnnotationClass == annotation.annotationClass.java) return annotation as T
return annotation.annotationClass.java.findAnnotationRecursively(
targetAnnotationClass,
searchedClasses,
) ?: return@forEach
}
// Search the super class.
superclass?.findAnnotationRecursively(
targetAnnotationClass,
searchedClasses,
)?.let { return it }
// Search the interfaces.
interfaces.forEach { superClass ->
return superClass.findAnnotationRecursively(
targetAnnotationClass,
searchedClasses,
) ?: return@forEach
}
return null
}
/**
* Search for an annotation recursively.
*
* First the annotations, then the annotated classes super class and then it's interfaces
* are searched for the annotation recursively.
*
* @param targetAnnotation The annotation to search for.
* @return The annotation if found, otherwise null.
*/
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: Class<T>): T? {
fun <T : Annotation> Class<*>.findAnnotationRecursively(
targetAnnotation: Class<T>,
searchedAnnotations: HashSet<Annotation>,
): T? {
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
@Suppress("UNCHECKED_CAST")
if (found != null) return found as T
for (annotation in this.annotations) {
if (searchedAnnotations.contains(annotation)) continue
searchedAnnotations.add(annotation)
return annotation.annotationClass.java.findAnnotationRecursively(
targetAnnotation,
searchedAnnotations
) ?: continue
}
return null
}
return this.findAnnotationRecursively(targetAnnotation, hashSetOf())
}
fun <T : Annotation> KClass<*>.findAnnotationRecursively(targetAnnotation: KClass<T>) =
java.findAnnotationRecursively(targetAnnotation.java)
}

View file

@ -9,7 +9,7 @@ object MethodFingerprintExtensions {
*/
@Deprecated(
message = "Use the property instead.",
replaceWith = ReplaceWith("this.fuzzyPatternScanMethod")
replaceWith = ReplaceWith("this.fuzzyPatternScanMethod"),
)
val MethodFingerprint.fuzzyPatternScanMethod
get() = this.fuzzyPatternScanMethod

View file

@ -48,7 +48,7 @@ abstract class MethodFingerprint(
*
* If the annotation is not present, this property is null.
*/
val fuzzyPatternScanMethod = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class.java)
val fuzzyPatternScanMethod = this::class.findAnnotationRecursively(FuzzyPatternScanMethod::class)
/**
* Resolve a [MethodFingerprint] using the lookup map built by [initializeLookupMaps].

View file

@ -5,9 +5,10 @@ package app.revanced.patcher.patch
import app.revanced.patcher.PatchClass
import app.revanced.patcher.Patcher
import app.revanced.patcher.data.Context
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
import app.revanced.patcher.patch.options.PatchOptions
import java.io.Closeable
import kotlin.reflect.full.findAnnotation
import app.revanced.patcher.patch.annotation.Patch as PatchAnnotation
/**
* A ReVanced patch.
@ -60,7 +61,7 @@ sealed class Patch<out T : Context<*>> {
val options = PatchOptions()
init {
this::class.findAnnotation<app.revanced.patcher.patch.annotation.Patch>()?.let { annotation ->
this::class.findAnnotationRecursively(PatchAnnotation::class)?.let { annotation ->
name = annotation.name.ifEmpty { null }
description = annotation.description.ifEmpty { null }
compatiblePackages =

View file

@ -0,0 +1,50 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertNull
private object AnnotationExtensionsTest {
@Test
fun `find annotation in annotated class`() {
assertNotNull(TestClasses.Annotation2::class.findAnnotationRecursively(TestClasses.Annotation::class))
}
@Test
fun `find annotation`() {
assertNotNull(TestClasses.AnnotatedClass::class.findAnnotationRecursively(TestClasses.Annotation::class))
}
@Test
fun `find annotation recursively in super class`() {
assertNotNull(TestClasses.AnnotatedClass2::class.findAnnotationRecursively(TestClasses.Annotation::class))
}
@Test
fun `find annotation recursively in super class with annotation`() {
assertNotNull(TestClasses.AnnotatedTestClass3::class.findAnnotationRecursively(TestClasses.Annotation::class))
}
@Test
fun `don't find unknown annotation in annotated class`() {
assertNull(TestClasses.AnnotatedClass::class.findAnnotationRecursively(TestClasses.UnknownAnnotation::class))
}
object TestClasses {
annotation class Annotation
@Annotation
annotation class Annotation2
annotation class UnknownAnnotation
@Annotation
abstract class AnnotatedClass
@Annotation2
class AnnotatedTestClass3
abstract class AnnotatedClass2 : AnnotatedClass()
}
}