diff --git a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt index f122bff..3c0e25e 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt @@ -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 Class<*>.findAnnotationRecursively( + targetAnnotationClass: Class, + searchedClasses: HashSet = 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 Class<*>.findAnnotationRecursively(targetAnnotation: Class): T? { - fun Class<*>.findAnnotationRecursively( - targetAnnotation: Class, - searchedAnnotations: HashSet, - ): 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 KClass<*>.findAnnotationRecursively(targetAnnotation: KClass) = + java.findAnnotationRecursively(targetAnnotation.java) } diff --git a/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt index a9a958a..96dc393 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt @@ -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 diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt index 04e511d..7e16df7 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt @@ -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]. diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 28e9b17..aed438c 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -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> { val options = PatchOptions() init { - this::class.findAnnotation()?.let { annotation -> + this::class.findAnnotationRecursively(PatchAnnotation::class)?.let { annotation -> name = annotation.name.ifEmpty { null } description = annotation.description.ifEmpty { null } compatiblePackages = diff --git a/src/test/kotlin/app/revanced/patcher/extensions/AnnotationExtensionsTest.kt b/src/test/kotlin/app/revanced/patcher/extensions/AnnotationExtensionsTest.kt new file mode 100644 index 0000000..5787201 --- /dev/null +++ b/src/test/kotlin/app/revanced/patcher/extensions/AnnotationExtensionsTest.kt @@ -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() + } +}