From 462fbe2cadf56d8b0dde33319256021093bd39d5 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 29 Nov 2023 20:26:20 +0100 Subject: [PATCH] feat: Add constructor to initialize patches without annotations --- api/revanced-patcher.api | 5 ++ .../app/revanced/patcher/PatchBundleLoader.kt | 5 +- .../revanced/patcher/patch/BytecodePatch.kt | 58 +++++++++++++++++-- .../app/revanced/patcher/patch/Patch.kt | 46 ++++++++++----- .../revanced/patcher/patch/ResourcePatch.kt | 34 ++++++++++- .../patcher/patch/PatchInitializationTest.kt | 29 ++++++++++ .../patcher/patch/options/PatchOptionsTest.kt | 1 + 7 files changed, 154 insertions(+), 24 deletions(-) create mode 100644 src/test/kotlin/app/revanced/patcher/patch/PatchInitializationTest.kt diff --git a/api/revanced-patcher.api b/api/revanced-patcher.api index 13017d1..573e5fe 100644 --- a/api/revanced-patcher.api +++ b/api/revanced-patcher.api @@ -234,11 +234,14 @@ public abstract interface annotation class app/revanced/patcher/fingerprint/anno public abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch { public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLjava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLjava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Ljava/util/Set;)V public synthetic fun (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public abstract class app/revanced/patcher/patch/Patch { + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public abstract fun execute (Lapp/revanced/patcher/data/Context;)V public final fun getCompatiblePackages ()Ljava/util/Set; @@ -272,6 +275,8 @@ public final class app/revanced/patcher/patch/PatchResult { public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch { public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V } public abstract interface annotation class app/revanced/patcher/patch/annotation/CompatiblePackage : java/lang/annotation/Annotation { diff --git a/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt b/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt index 7d3075f..2fd29d6 100644 --- a/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt +++ b/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt @@ -12,6 +12,7 @@ import java.util.jar.JarFile import java.util.logging.Logger import kotlin.reflect.KClass + /** * A set of [Patch]es. */ @@ -73,7 +74,7 @@ sealed class PatchBundleLoader private constructor( if (!silent) { logger.fine( "Patch class '$name' has no INSTANCE field, therefor not a singleton. " + - "Will try to instantiate it.", + "Attempting to instantiate it.", ) } @@ -83,7 +84,7 @@ sealed class PatchBundleLoader private constructor( if (!silent) { logger.severe( "Patch class '$name' is not singleton and has no suitable constructor, " + - "therefor cannot be instantiated and will be ignored.", + "therefor cannot be instantiated and is ignored.", ) } diff --git a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt index d13c8a5..897b60b 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt @@ -1,13 +1,61 @@ package app.revanced.patcher.patch +import app.revanced.patcher.PatchClass +import app.revanced.patcher.Patcher import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.fingerprint.MethodFingerprint +import java.io.Closeable /** - * A ReVanced [Patch] that works on [BytecodeContext]. + * A ReVanced [Patch] that accesses a [BytecodeContext]. * - * @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed. + * If an implementation of [Patch] also implements [Closeable] + * it will be closed in reverse execution order of patches executed by ReVanced [Patcher]. */ -abstract class BytecodePatch( - internal val fingerprints: Set = emptySet(), -) : Patch() +@Suppress("unused") +abstract class BytecodePatch : Patch { + /** + * The fingerprints to resolve before executing the patch. + */ + internal val fingerprints: Set + + /** + * Create a new [BytecodePatch]. + * + * @param fingerprints The fingerprints to resolve before executing the patch. + */ + constructor(fingerprints: Set = emptySet()) { + this.fingerprints = fingerprints + } + + /** + * Create a new [BytecodePatch]. + * + * @param name The name of the patch. + * @param description The description of the patch. + * @param compatiblePackages The packages the patch is compatible with. + * @param dependencies Other patches this patch depends on. + * @param use Weather or not the patch should be used. + * @param requiresIntegrations Weather or not the patch requires integrations. + */ + constructor( + name: String? = null, + description: String? = null, + compatiblePackages: Set? = null, + dependencies: Set? = null, + use: Boolean = true, + requiresIntegrations: Boolean = false, + fingerprints: Set = emptySet(), + ) : super(name, description, compatiblePackages, dependencies, use, requiresIntegrations) { + this.fingerprints = fingerprints + } + + /** + * Create a new [BytecodePatch]. + */ + @Deprecated( + "Use the constructor with fingerprints instead.", + ReplaceWith("BytecodePatch(emptySet())"), + ) + constructor() : this(emptySet()) +} diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index aed438c..256f2fd 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -8,10 +8,10 @@ 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 app.revanced.patcher.patch.annotation.Patch as PatchAnnotation /** * A ReVanced patch. + * * If an implementation of [Patch] also implements [Closeable] * it will be closed in reverse execution order of patches executed by ReVanced [Patcher]. * @@ -55,25 +55,41 @@ sealed class Patch> { var requiresIntegrations = false private set + constructor( + name: String?, + description: String?, + compatiblePackages: Set?, + dependencies: Set?, + use: Boolean, + requiresIntegrations: Boolean, + ) { + this.name = name + this.description = description + this.compatiblePackages = compatiblePackages + this.dependencies = dependencies + this.use = use + this.requiresIntegrations = requiresIntegrations + } + + constructor() { + this::class.findAnnotationRecursively(app.revanced.patcher.patch.annotation.Patch::class)?.let { annotation -> + this.name = annotation.name.ifEmpty { null } + this.description = annotation.description.ifEmpty { null } + this.compatiblePackages = + annotation.compatiblePackages + .map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) } + .toSet().ifEmpty { null } + this.dependencies = annotation.dependencies.toSet().ifEmpty { null } + this.use = annotation.use + this.requiresIntegrations = annotation.requiresIntegrations + } + } + /** * The options of the patch associated by the options key. */ val options = PatchOptions() - init { - this::class.findAnnotationRecursively(PatchAnnotation::class)?.let { annotation -> - name = annotation.name.ifEmpty { null } - description = annotation.description.ifEmpty { null } - compatiblePackages = - annotation.compatiblePackages - .map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) } - .toSet().ifEmpty { null } - dependencies = annotation.dependencies.toSet().ifEmpty { null } - use = annotation.use - requiresIntegrations = annotation.requiresIntegrations - } - } - /** * The execution function of the patch. * diff --git a/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt b/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt index e86ebfd..f0ed13e 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt @@ -1,8 +1,38 @@ package app.revanced.patcher.patch +import app.revanced.patcher.PatchClass +import app.revanced.patcher.Patcher import app.revanced.patcher.data.ResourceContext +import java.io.Closeable /** - * A ReVanced [Patch] that works on [ResourceContext]. + * A ReVanced [Patch] that accesses a [ResourceContext]. + * + * If an implementation of [Patch] also implements [Closeable] + * it will be closed in reverse execution order of patches executed by ReVanced [Patcher]. */ -abstract class ResourcePatch : Patch() +abstract class ResourcePatch : Patch { + /** + * Create a new [ResourcePatch]. + */ + constructor() + + /** + * Create a new [ResourcePatch]. + * + * @param name The name of the patch. + * @param description The description of the patch. + * @param compatiblePackages The packages the patch is compatible with. + * @param dependencies Other patches this patch depends on. + * @param use Weather or not the patch should be used. + * @param requiresIntegrations Weather or not the patch requires integrations. + */ + constructor( + name: String? = null, + description: String? = null, + compatiblePackages: Set? = null, + dependencies: Set? = null, + use: Boolean = true, + requiresIntegrations: Boolean = false, + ) : super(name, description, compatiblePackages, dependencies, use, requiresIntegrations) +} diff --git a/src/test/kotlin/app/revanced/patcher/patch/PatchInitializationTest.kt b/src/test/kotlin/app/revanced/patcher/patch/PatchInitializationTest.kt new file mode 100644 index 0000000..f780175 --- /dev/null +++ b/src/test/kotlin/app/revanced/patcher/patch/PatchInitializationTest.kt @@ -0,0 +1,29 @@ +package app.revanced.patcher.patch + +import app.revanced.patcher.data.ResourceContext +import org.junit.jupiter.api.assertThrows +import kotlin.test.Test +import app.revanced.patcher.patch.annotation.Patch as PatchAnnotation + +object PatchInitializationTest { + @Test + fun `initialize using constructor`() { + val patch = + object : ResourcePatch(name = "Resource patch test") { + override fun execute(context: ResourceContext) {} + } + + assert(patch.name == "Resource patch test") + } + + @Test + fun `initialize using annotation`() { + val patch = + @PatchAnnotation("Resource patch test") + object : ResourcePatch() { + override fun execute(context: ResourceContext) {} + } + + assert(patch.name == "Resource patch test") + } +} diff --git a/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt b/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt index d7fccb1..6b89602 100644 --- a/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt +++ b/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt @@ -90,6 +90,7 @@ internal class PatchOptionsTest { @Test fun `getting default value should work`() = assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) } + @Suppress("DEPRECATION") private object OptionsTestPatch : BytecodePatch() { var booleanOption by booleanPatchOption( "bool",