feat: Add constructor to initialize patches without annotations

This commit is contained in:
oSumAtrIX 2023-11-29 20:26:20 +01:00
parent 7aeae93f3d
commit 462fbe2cad
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
7 changed files with 154 additions and 24 deletions

View file

@ -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 <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLjava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLjava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/util/Set;)V
public synthetic fun <init> (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}
public abstract class app/revanced/patcher/patch/Patch {
public synthetic fun <init> (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 <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V
public synthetic fun <init> (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 {

View file

@ -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.",
)
}

View file

@ -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<MethodFingerprint> = emptySet(),
) : Patch<BytecodeContext>()
@Suppress("unused")
abstract class BytecodePatch : Patch<BytecodeContext> {
/**
* The fingerprints to resolve before executing the patch.
*/
internal val fingerprints: Set<MethodFingerprint>
/**
* Create a new [BytecodePatch].
*
* @param fingerprints The fingerprints to resolve before executing the patch.
*/
constructor(fingerprints: Set<MethodFingerprint> = 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<CompatiblePackage>? = null,
dependencies: Set<PatchClass>? = null,
use: Boolean = true,
requiresIntegrations: Boolean = false,
fingerprints: Set<MethodFingerprint> = 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())
}

View file

@ -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<out T : Context<*>> {
var requiresIntegrations = false
private set
constructor(
name: String?,
description: String?,
compatiblePackages: Set<CompatiblePackage>?,
dependencies: Set<PatchClass>?,
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.
*

View file

@ -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<ResourceContext>()
abstract class ResourcePatch : Patch<ResourceContext> {
/**
* 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<CompatiblePackage>? = null,
dependencies: Set<PatchClass>? = null,
use: Boolean = true,
requiresIntegrations: Boolean = false,
) : super(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
}

View file

@ -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")
}
}

View file

@ -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",