mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 01:02:22 +01:00
feat!: Use Kotlin DSL for patch options (#234)
This commit is contained in:
parent
fcc1de45ed
commit
c299817193
30 changed files with 1055 additions and 724 deletions
|
@ -3,7 +3,6 @@ package app.revanced.patcher.patch.annotations.processor
|
|||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchOptions
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import com.google.devtools.ksp.processing.*
|
||||
|
@ -114,7 +113,6 @@ class PatchProcessor(
|
|||
}
|
||||
}
|
||||
|
||||
// kotlin poet generate a class for each patch
|
||||
executablePatches.forEach { (patchDeclaration, patchAnnotation) ->
|
||||
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
||||
|
||||
|
@ -159,7 +157,7 @@ class PatchProcessor(
|
|||
"dependencies = setOf(%L)",
|
||||
buildList {
|
||||
addAll(dependencies)
|
||||
// Also add the source class of the generated class so that it is also executed
|
||||
// Also add the source class of the generated class so that it is also executed.
|
||||
add(patchDeclaration.toClassName())
|
||||
}.joinToString(", ") { dependency ->
|
||||
"${(dependencyResolutionMap[dependency] ?: dependency)}::class"
|
||||
|
@ -181,9 +179,18 @@ class PatchProcessor(
|
|||
.addParameter("context", contextClass)
|
||||
.build()
|
||||
)
|
||||
.addProperty(
|
||||
PropertySpec.builder("options", PatchOptions::class, KModifier.OVERRIDE)
|
||||
.initializer("%T.options", patchDeclaration.toClassName())
|
||||
.addInitializerBlock(
|
||||
CodeBlock.builder()
|
||||
.add(
|
||||
"%T.options.forEach { (key, option) ->",
|
||||
patchDeclaration.toClassName()
|
||||
)
|
||||
.addStatement(
|
||||
"options.register(option)"
|
||||
)
|
||||
.add(
|
||||
"}"
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
|
|
@ -7,7 +7,6 @@ import com.tschuchort.compiletesting.kspWithCompilation
|
|||
import com.tschuchort.compiletesting.symbolProcessorProviders
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class TestPatchAnnotationProcessor {
|
||||
|
@ -64,7 +63,7 @@ class TestPatchAnnotationProcessor {
|
|||
)
|
||||
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
||||
|
||||
assertNotNull(patch.options)
|
||||
assert(patch.options.isNotEmpty())
|
||||
assertEquals(patch.options["print"].title, "Print message")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package app.revanced.patcher.patch.annotations.processor.samples.options
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchOption
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
||||
|
||||
@Patch(name = "Options patch")
|
||||
object OptionsPatch : ResourcePatch() {
|
||||
override fun execute(context: ResourceContext) {}
|
||||
|
||||
@Suppress("unused")
|
||||
private val printOption by option(
|
||||
PatchOption.StringOption(
|
||||
"print",
|
||||
null,
|
||||
"Print message",
|
||||
"The message to print."
|
||||
)
|
||||
private val printOption by stringPatchOption(
|
||||
"print",
|
||||
null,
|
||||
"Print message",
|
||||
"The message to print."
|
||||
)
|
||||
}
|
|
@ -332,29 +332,7 @@ public abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/pa
|
|||
public synthetic fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/IllegalValueException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/Object;)V
|
||||
public final fun getValue ()Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/InvalidTypeException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||
public final fun getExpected ()Ljava/lang/String;
|
||||
public final fun getGot ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/NoSuchOptionException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public final fun getOption ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/OptionsContainer {
|
||||
public fun <init> ()V
|
||||
public fun getOptions ()Lapp/revanced/patcher/patch/PatchOptions;
|
||||
protected final fun option (Lapp/revanced/patcher/patch/PatchOption;)Lapp/revanced/patcher/patch/PatchOption;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/Patch : app/revanced/patcher/patch/OptionsContainer {
|
||||
public abstract class app/revanced/patcher/patch/Patch {
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
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
|
||||
|
@ -363,6 +341,7 @@ public abstract class app/revanced/patcher/patch/Patch : app/revanced/patcher/pa
|
|||
public final fun getDependencies ()Ljava/util/Set;
|
||||
public final fun getDescription ()Ljava/lang/String;
|
||||
public final fun getName ()Ljava/lang/String;
|
||||
public final fun getOptions ()Lapp/revanced/patcher/patch/options/PatchOptions;
|
||||
public final fun getRequiresIntegrations ()Z
|
||||
public final fun getUse ()Z
|
||||
public fun hashCode ()I
|
||||
|
@ -381,61 +360,9 @@ public final class app/revanced/patcher/patch/PatchException : java/lang/Excepti
|
|||
public fun <init> (Ljava/lang/Throwable;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/PatchOption {
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun getDescription ()Ljava/lang/String;
|
||||
public final fun getKey ()Ljava/lang/String;
|
||||
public final fun getRequired ()Z
|
||||
public final fun getTitle ()Ljava/lang/String;
|
||||
public final fun getValidator ()Lkotlin/jvm/functions/Function1;
|
||||
public final fun getValue ()Ljava/lang/Object;
|
||||
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
||||
public final fun setValue (Ljava/lang/Object;)V
|
||||
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchOption$BooleanOption : app/revanced/patcher/patch/PatchOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchOption$IntListOption : app/revanced/patcher/patch/PatchOption$ListOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/PatchOption$ListOption : app/revanced/patcher/patch/PatchOption {
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun getOptions ()Ljava/lang/Iterable;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchOption$StringListOption : app/revanced/patcher/patch/PatchOption$ListOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchOption$StringOption : app/revanced/patcher/patch/PatchOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchOptions : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public fun <init> ([Lapp/revanced/patcher/patch/PatchOption;)V
|
||||
public final fun getUntyped (Ljava/lang/String;)Lapp/revanced/patcher/patch/PatchOption;
|
||||
public fun iterator ()Ljava/util/Iterator;
|
||||
public final fun nullify (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchResult {
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
||||
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
public fun hashCode ()I
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/RequirementNotMetException : java/lang/Exception {
|
||||
public static final field INSTANCE Lapp/revanced/patcher/patch/RequirementNotMetException;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
||||
|
@ -444,6 +371,167 @@ public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/pa
|
|||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/options/PatchOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||
public final fun getDescription ()Ljava/lang/String;
|
||||
public final fun getKey ()Ljava/lang/String;
|
||||
public final fun getRequired ()Z
|
||||
public final fun getTitle ()Ljava/lang/String;
|
||||
public final fun getValidate ()Lkotlin/jvm/functions/Function1;
|
||||
public final fun getValue ()Ljava/lang/Object;
|
||||
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
||||
public final fun setValue (Ljava/lang/Object;)V
|
||||
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/options/PatchOptionException : java/lang/Exception {
|
||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/PatchOptionException$InvalidValueTypeException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/PatchOptionException$PatchOptionNotFoundException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueRequiredException : java/lang/Exception {
|
||||
public fun <init> (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueValidationException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/PatchOptions : java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
|
||||
public fun <init> ()V
|
||||
public fun clear ()V
|
||||
public final fun containsKey (Ljava/lang/Object;)Z
|
||||
public fun containsKey (Ljava/lang/String;)Z
|
||||
public fun containsValue (Lapp/revanced/patcher/patch/options/PatchOption;)Z
|
||||
public final fun containsValue (Ljava/lang/Object;)Z
|
||||
public final fun entrySet ()Ljava/util/Set;
|
||||
public final fun get (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
|
||||
public fun get (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||
public fun getEntries ()Ljava/util/Set;
|
||||
public fun getKeys ()Ljava/util/Set;
|
||||
public fun getSize ()I
|
||||
public fun getValues ()Ljava/util/Collection;
|
||||
public fun isEmpty ()Z
|
||||
public final fun keySet ()Ljava/util/Set;
|
||||
public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
public fun put (Ljava/lang/String;Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||
public fun putAll (Ljava/util/Map;)V
|
||||
public final fun register (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||
public final fun remove (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||
public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
|
||||
public fun remove (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||
public final fun size ()I
|
||||
public final fun values ()Ljava/util/Collection;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption$Companion {
|
||||
public final fun booleanPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
||||
public static synthetic fun booleanPatchOption$default (Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/FloatPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/FloatPatchOption$Companion {
|
||||
public final fun floatPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
||||
public static synthetic fun floatPatchOption$default (Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/IntPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/IntPatchOption$Companion {
|
||||
public final fun intPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
||||
public static synthetic fun intPatchOption$default (Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/LongPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/LongPatchOption$Companion {
|
||||
public final fun longPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
||||
public static synthetic fun longPatchOption$default (Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/StringPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/StringPatchOption$Companion {
|
||||
public final fun stringPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
||||
public static synthetic fun stringPatchOption$default (Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion {
|
||||
public final fun booleanArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
||||
public static synthetic fun booleanArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion {
|
||||
public final fun floatArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
||||
public static synthetic fun floatArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion {
|
||||
public final fun intArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
||||
public static synthetic fun intArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion {
|
||||
public final fun longArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
||||
public static synthetic fun longArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;
|
||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion {
|
||||
public final fun stringArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
||||
public static synthetic fun stringArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/DomFileEditor : java/io/Closeable {
|
||||
public fun <init> (Ljava/io/File;)V
|
||||
public fun close ()V
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package app.revanced.patcher.patch
|
||||
|
||||
import app.revanced.patcher.PatchClass
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
||||
/**
|
||||
* A ReVanced [Patch] that works on [BytecodeContext].
|
||||
*
|
||||
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
||||
* @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 The names of patches this patch depends on.
|
||||
* @param use Weather or not the patch should be used.
|
||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||
*/
|
||||
abstract class BytecodePatch(
|
||||
internal val fingerprints: Set<MethodFingerprint> = emptySet(),
|
||||
name: String? = null,
|
||||
description: String? = null,
|
||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||
dependencies: Set<PatchClass>? = null,
|
||||
use: Boolean = true,
|
||||
// TODO: Remove this property, once integrations are coupled with patches.
|
||||
requiresIntegrations: Boolean = false,
|
||||
) : Patch<BytecodeContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
@ -1,23 +0,0 @@
|
|||
package app.revanced.patcher.patch
|
||||
|
||||
/**
|
||||
* A container for patch options.
|
||||
*/
|
||||
abstract class OptionsContainer {
|
||||
/**
|
||||
* A list of [PatchOption]s.
|
||||
* @see PatchOptions
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open val options = PatchOptions()
|
||||
|
||||
/**
|
||||
* Registers a [PatchOption].
|
||||
* @param opt The [PatchOption] to register.
|
||||
* @return The registered [PatchOption].
|
||||
*/
|
||||
protected fun <T> option(opt: PatchOption<T>): PatchOption<T> {
|
||||
options.register(opt)
|
||||
return opt
|
||||
}
|
||||
}
|
|
@ -4,10 +4,8 @@ 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.data.Context
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.options.PatchOptions
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
|
@ -32,7 +30,12 @@ sealed class Patch<out T : Context<*>>(
|
|||
val use: Boolean = true,
|
||||
// TODO: Remove this property, once integrations are coupled with patches.
|
||||
val requiresIntegrations: Boolean = false,
|
||||
) : OptionsContainer() {
|
||||
) {
|
||||
/**
|
||||
* The options of the patch associated by the options key.
|
||||
*/
|
||||
val options = PatchOptions()
|
||||
|
||||
/**
|
||||
* The execution function of the patch.
|
||||
*
|
||||
|
@ -66,44 +69,3 @@ sealed class Patch<out T : Context<*>>(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReVanced [Patch] that works on [ResourceContext].
|
||||
*
|
||||
* @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 The names of patches this patch depends on.
|
||||
* @param use Weather or not the patch should be used.
|
||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||
*/
|
||||
abstract class ResourcePatch(
|
||||
name: String? = null,
|
||||
description: String? = null,
|
||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||
dependencies: Set<PatchClass>? = null,
|
||||
use: Boolean = true,
|
||||
// TODO: Remove this property, once integrations are coupled with patches.
|
||||
requiresIntegrations: Boolean = false,
|
||||
) : Patch<ResourceContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
||||
|
||||
/**
|
||||
* A ReVanced [Patch] that works on [BytecodeContext].
|
||||
*
|
||||
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
||||
* @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 The names of patches this patch depends on.
|
||||
* @param use Weather or not the patch should be used.
|
||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||
*/
|
||||
abstract class BytecodePatch(
|
||||
internal val fingerprints: Set<MethodFingerprint> = emptySet(),
|
||||
name: String? = null,
|
||||
description: String? = null,
|
||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||
dependencies: Set<PatchClass>? = null,
|
||||
use: Boolean = true,
|
||||
// TODO: Remove this property, once integrations are coupled with patches.
|
||||
requiresIntegrations: Boolean = false,
|
||||
) : Patch<BytecodeContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
@ -1,230 +0,0 @@
|
|||
@file:Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST")
|
||||
|
||||
package app.revanced.patcher.patch
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
||||
class IllegalValueException(val value: Any?) : Exception("Illegal value: $value")
|
||||
class InvalidTypeException(val got: String, val expected: String) :
|
||||
Exception("Invalid option value type: $got, expected $expected")
|
||||
|
||||
object RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
||||
|
||||
/**
|
||||
* A registry for an array of [PatchOption]s.
|
||||
* @param options An array of [PatchOption]s.
|
||||
*/
|
||||
class PatchOptions(vararg options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
||||
private val register = mutableMapOf<String, PatchOption<*>>()
|
||||
|
||||
init {
|
||||
options.forEach { register(it) }
|
||||
}
|
||||
|
||||
internal fun register(option: PatchOption<*>) {
|
||||
if (register.containsKey(option.key)) {
|
||||
throw IllegalStateException("Multiple options found with the same key")
|
||||
}
|
||||
register[option.key] = option
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a [PatchOption] by its key.
|
||||
* @param key The key of the [PatchOption].
|
||||
*/
|
||||
@JvmName("getUntyped")
|
||||
operator fun get(key: String) = register[key] ?: throw NoSuchOptionException(key)
|
||||
|
||||
/**
|
||||
* Get a [PatchOption] by its key.
|
||||
* @param key The key of the [PatchOption].
|
||||
*/
|
||||
inline operator fun <reified T> get(key: String): PatchOption<T> {
|
||||
val opt = get(key)
|
||||
if (opt.value !is T) throw InvalidTypeException(
|
||||
opt.value?.let { it::class.java.canonicalName } ?: "null",
|
||||
T::class.java.canonicalName
|
||||
)
|
||||
return opt as PatchOption<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a [PatchOption].
|
||||
* @param key The key of the [PatchOption].
|
||||
* @param value The value you want it to be.
|
||||
* Please note that using the wrong value type results in a runtime error.
|
||||
*/
|
||||
inline operator fun <reified T> set(key: String, value: T) {
|
||||
val opt = get<T>(key)
|
||||
if (opt.value !is T) throw InvalidTypeException(
|
||||
T::class.java.canonicalName,
|
||||
opt.value?.let { it::class.java.canonicalName } ?: "null"
|
||||
)
|
||||
opt.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a [PatchOption] to `null`.
|
||||
* @param key The key of the [PatchOption].
|
||||
*/
|
||||
fun nullify(key: String) {
|
||||
get(key).value = null
|
||||
}
|
||||
|
||||
override fun iterator() = register.values.iterator()
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Patch] option.
|
||||
* @param key Unique identifier of the option. Example: _`settings`_
|
||||
* @param default The default value of the option.
|
||||
* @param title A human-readable title of the option. Example: _Patch Settings_
|
||||
* @param description A human-readable description of the option. Example: _Settings for the patches._
|
||||
* @param required Whether the option is required.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
sealed class PatchOption<T>(
|
||||
val key: String,
|
||||
default: T?,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val required: Boolean,
|
||||
val validator: (T?) -> Boolean
|
||||
) {
|
||||
var value: T? = default
|
||||
get() {
|
||||
if (field == null && required) {
|
||||
throw RequirementNotMetException
|
||||
}
|
||||
return field
|
||||
}
|
||||
set(value) {
|
||||
if (value == null && required) {
|
||||
throw RequirementNotMetException
|
||||
}
|
||||
if (!validator(value)) {
|
||||
throw IllegalValueException(value)
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the option.
|
||||
* Please note that using the wrong value type results in a runtime error.
|
||||
*/
|
||||
@JvmName("getValueTyped")
|
||||
inline operator fun <reified V> getValue(thisRef: Nothing?, property: KProperty<*>): V? {
|
||||
if (value !is V?) throw InvalidTypeException(
|
||||
V::class.java.canonicalName,
|
||||
value?.let { it::class.java.canonicalName } ?: "null"
|
||||
)
|
||||
return value as? V?
|
||||
}
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||
|
||||
/**
|
||||
* Gets the value of the option.
|
||||
* Please note that using the wrong value type results in a runtime error.
|
||||
*/
|
||||
@JvmName("setValueTyped")
|
||||
inline operator fun <reified V> setValue(thisRef: Nothing?, property: KProperty<*>, new: V) {
|
||||
if (value !is V) throw InvalidTypeException(
|
||||
V::class.java.canonicalName,
|
||||
value?.let { it::class.java.canonicalName } ?: "null"
|
||||
)
|
||||
value = new as T
|
||||
}
|
||||
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, new: T?) {
|
||||
value = new
|
||||
}
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [String].
|
||||
* @see PatchOption
|
||||
*/
|
||||
class StringOption(
|
||||
key: String,
|
||||
default: String?,
|
||||
title: String,
|
||||
description: String,
|
||||
required: Boolean = false,
|
||||
validator: (String?) -> Boolean = { true }
|
||||
) : PatchOption<String>(
|
||||
key, default, title, description, required, validator
|
||||
)
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Boolean].
|
||||
* @see PatchOption
|
||||
*/
|
||||
class BooleanOption(
|
||||
key: String,
|
||||
default: Boolean?,
|
||||
title: String,
|
||||
description: String,
|
||||
required: Boolean = false,
|
||||
validator: (Boolean?) -> Boolean = { true }
|
||||
) : PatchOption<Boolean>(
|
||||
key, default, title, description, required, validator
|
||||
)
|
||||
|
||||
/**
|
||||
* A [PatchOption] with a list of allowed options.
|
||||
* @param options A list of allowed options for the [ListOption].
|
||||
* @see PatchOption
|
||||
*/
|
||||
sealed class ListOption<E>(
|
||||
key: String,
|
||||
default: E?,
|
||||
val options: Iterable<E>,
|
||||
title: String,
|
||||
description: String,
|
||||
required: Boolean = false,
|
||||
validator: (E?) -> Boolean = { true }
|
||||
) : PatchOption<E>(
|
||||
key, default, title, description, required, {
|
||||
(it?.let { it in options } ?: true) && validator(it)
|
||||
}
|
||||
) {
|
||||
init {
|
||||
if (default != null && default !in options) {
|
||||
throw IllegalStateException("Default option must be an allowed option")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [ListOption] of type [String].
|
||||
* @see ListOption
|
||||
*/
|
||||
class StringListOption(
|
||||
key: String,
|
||||
default: String?,
|
||||
options: Iterable<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
required: Boolean = false,
|
||||
validator: (String?) -> Boolean = { true }
|
||||
) : ListOption<String>(
|
||||
key, default, options, title, description, required, validator
|
||||
)
|
||||
|
||||
/**
|
||||
* A [ListOption] of type [Int].
|
||||
* @see ListOption
|
||||
*/
|
||||
class IntListOption(
|
||||
key: String,
|
||||
default: Int?,
|
||||
options: Iterable<Int>,
|
||||
title: String,
|
||||
description: String,
|
||||
required: Boolean = false,
|
||||
validator: (Int?) -> Boolean = { true }
|
||||
) : ListOption<Int>(
|
||||
key, default, options, title, description, required, validator
|
||||
)
|
||||
}
|
|
@ -7,14 +7,4 @@ package app.revanced.patcher.patch
|
|||
* @param exception The [PatchException] thrown, if any.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) {
|
||||
override fun hashCode() = patch.hashCode()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as PatchResult
|
||||
|
||||
return patch == other.patch
|
||||
}
|
||||
}
|
||||
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null)
|
|
@ -0,0 +1,24 @@
|
|||
package app.revanced.patcher.patch
|
||||
|
||||
import app.revanced.patcher.PatchClass
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
|
||||
/**
|
||||
* A ReVanced [Patch] that works on [ResourceContext].
|
||||
*
|
||||
* @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 The names of patches this patch depends on.
|
||||
* @param use Weather or not the patch should be used.
|
||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||
*/
|
||||
abstract class ResourcePatch(
|
||||
name: String? = null,
|
||||
description: String? = null,
|
||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||
dependencies: Set<PatchClass>? = null,
|
||||
use: Boolean = true,
|
||||
// TODO: Remove this property, once integrations are coupled with patches.
|
||||
requiresIntegrations: Boolean = false,
|
||||
) : Patch<ResourceContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
@ -0,0 +1,40 @@
|
|||
package app.revanced.patcher.patch.options
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* A [Patch] option.
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @param validate The function to validate values of the option.
|
||||
* @param T The value type of the option.
|
||||
*/
|
||||
abstract class PatchOption<T>(
|
||||
val key: String,
|
||||
default: T?,
|
||||
val title: String?,
|
||||
val description: String?,
|
||||
val required: Boolean,
|
||||
val validate: (T?) -> Boolean
|
||||
) {
|
||||
/**
|
||||
* The value of the [PatchOption].
|
||||
*/
|
||||
var value: T? = default
|
||||
set(value) {
|
||||
if (required && value == null) throw PatchOptionException.ValueRequiredException(this)
|
||||
if (!validate(value)) throw PatchOptionException.ValueValidationException(value, this)
|
||||
|
||||
field = value
|
||||
}
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||
this.value = value
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package app.revanced.patcher.patch.options
|
||||
|
||||
/**
|
||||
* An exception thrown when using [PatchOption]s.
|
||||
*
|
||||
* @param errorMessage The exception message.
|
||||
*/
|
||||
sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage, null) {
|
||||
/**
|
||||
* An exception thrown when a [PatchOption] is set to an invalid value.
|
||||
*
|
||||
* @param invalidType The type of the value that was passed.
|
||||
* @param expectedType The type of the value that was expected.
|
||||
*/
|
||||
class InvalidValueTypeException(invalidType: String, expectedType: String) :
|
||||
PatchOptionException("Type $expectedType was expected but received type $invalidType")
|
||||
|
||||
/**
|
||||
* An exception thrown when a value did not satisfy the value conditions specified by the [PatchOption].
|
||||
*
|
||||
* @param value The value that failed validation.
|
||||
*/
|
||||
class ValueValidationException(value: Any?, option: PatchOption<*>) :
|
||||
Exception("The option value \"$value\" failed validation for ${option.key}")
|
||||
|
||||
/**
|
||||
* An exception thrown when a value is required but null was passed.
|
||||
*
|
||||
* @param option The [PatchOption] that requires a value.
|
||||
*/
|
||||
class ValueRequiredException(option: PatchOption<*>) :
|
||||
Exception("The option ${option.key} requires a value, but null was passed")
|
||||
|
||||
/**
|
||||
* An exception thrown when a [PatchOption] is not found.
|
||||
*
|
||||
* @param key The key of the [PatchOption].
|
||||
*/
|
||||
class PatchOptionNotFoundException(key: String)
|
||||
: Exception("No option with key $key")
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package app.revanced.patcher.patch.options
|
||||
|
||||
|
||||
/**
|
||||
* A map of [PatchOption]s associated by their keys.
|
||||
*
|
||||
* @param options The [PatchOption]s to initialize with.
|
||||
*/
|
||||
class PatchOptions internal constructor(
|
||||
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf()
|
||||
) : MutableMap<String, PatchOption<*>> by options {
|
||||
/**
|
||||
* Register a [PatchOption]. Acts like [MutableMap.put].
|
||||
* @param value The [PatchOption] to register.
|
||||
*/
|
||||
fun register(value: PatchOption<*>) {
|
||||
options[value.key] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an option's value.
|
||||
* @param key The identifier.
|
||||
* @param value The value.
|
||||
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
||||
*/
|
||||
inline operator fun <reified T: Any> set(key: String, value: T?) {
|
||||
val option = this[key]
|
||||
|
||||
if (option.value !is T) throw PatchOptionException.InvalidValueTypeException(
|
||||
T::class.java.name,
|
||||
option.value?.let { it::class.java.name } ?: "null",
|
||||
)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(option as PatchOption<T>).value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an option.
|
||||
*/
|
||||
override operator fun get(key: String) =
|
||||
options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Boolean].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class BooleanPatchOption private constructor(
|
||||
key: String,
|
||||
default: Boolean?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Boolean?) -> Boolean
|
||||
) : PatchOption<Boolean>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [BooleanPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [BooleanPatchOption].
|
||||
*
|
||||
* @see BooleanPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.booleanPatchOption(
|
||||
key: String,
|
||||
default: Boolean? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Boolean?) -> Boolean = { true }
|
||||
) = BooleanPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Float].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class FloatPatchOption private constructor(
|
||||
key: String,
|
||||
default: Float?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Float?) -> Boolean
|
||||
) : PatchOption<Float>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [FloatPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [FloatPatchOption].
|
||||
*
|
||||
* @see FloatPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.floatPatchOption(
|
||||
key: String,
|
||||
default: Float? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Float?) -> Boolean = { true }
|
||||
) = FloatPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing an [Integer].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class IntPatchOption private constructor(
|
||||
key: String,
|
||||
default: Int?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Int?) -> Boolean
|
||||
) : PatchOption<Int>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [IntPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [IntPatchOption].
|
||||
*
|
||||
* @see IntPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.intPatchOption(
|
||||
key: String,
|
||||
default: Int? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Int?) -> Boolean = { true }
|
||||
) = IntPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Long].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class LongPatchOption private constructor(
|
||||
key: String,
|
||||
default: Long?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Long?) -> Boolean
|
||||
) : PatchOption<Long>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [LongPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [LongPatchOption].
|
||||
*
|
||||
* @see LongPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.longPatchOption(
|
||||
key: String,
|
||||
default: Long? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Long?) -> Boolean = { true }
|
||||
) = LongPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [String].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class StringPatchOption private constructor(
|
||||
key: String,
|
||||
default: String?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (String?) -> Boolean
|
||||
) : PatchOption<String>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [StringPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [StringPatchOption].
|
||||
*
|
||||
* @see StringPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.stringPatchOption(
|
||||
key: String,
|
||||
default: String? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (String?) -> Boolean = { true }
|
||||
) = StringPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types.array
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Boolean] array.
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class BooleanArrayPatchOption private constructor(
|
||||
key: String,
|
||||
default: Array<Boolean>?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Array<Boolean>?) -> Boolean
|
||||
) : PatchOption<Array<Boolean>>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [BooleanArrayPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [BooleanArrayPatchOption].
|
||||
*
|
||||
* @see BooleanArrayPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.booleanArrayPatchOption(
|
||||
key: String,
|
||||
default: Array<Boolean>? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Array<Boolean>?) -> Boolean = { true }
|
||||
) = BooleanArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types.array
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Float] array.
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class FloatArrayPatchOption private constructor(
|
||||
key: String,
|
||||
default: Array<Float>?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Array<Float>?) -> Boolean
|
||||
) : PatchOption<Array<Float>>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [FloatArrayPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [FloatArrayPatchOption].
|
||||
*
|
||||
* @see FloatArrayPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.floatArrayPatchOption(
|
||||
key: String,
|
||||
default: Array<Float>? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Array<Float>?) -> Boolean = { true }
|
||||
) = FloatArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types.array
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing an [Integer] array.
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class IntArrayPatchOption private constructor(
|
||||
key: String,
|
||||
default: Array<Int>?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Array<Int>?) -> Boolean
|
||||
) : PatchOption<Array<Int>>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [IntArrayPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [IntArrayPatchOption].
|
||||
*
|
||||
* @see IntArrayPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.intArrayPatchOption(
|
||||
key: String,
|
||||
default: Array<Int>? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Array<Int>?) -> Boolean = { true }
|
||||
) = IntArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types.array
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [Long] array.
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class LongArrayPatchOption private constructor(
|
||||
key: String,
|
||||
default: Array<Long>?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Array<Long>?) -> Boolean
|
||||
) : PatchOption<Array<Long>>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [LongArrayPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [LongArrayPatchOption].
|
||||
*
|
||||
* @see LongArrayPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.longArrayPatchOption(
|
||||
key: String,
|
||||
default: Array<Long>? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Array<Long>?) -> Boolean = { true }
|
||||
) = LongArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package app.revanced.patcher.patch.options.types.array
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
|
||||
/**
|
||||
* A [PatchOption] representing a [String] array.
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
*
|
||||
* @see PatchOption
|
||||
*/
|
||||
class StringArrayPatchOption private constructor(
|
||||
key: String,
|
||||
default: Array<String>?,
|
||||
title: String?,
|
||||
description: String?,
|
||||
required: Boolean,
|
||||
validator: (Array<String>?) -> Boolean
|
||||
) : PatchOption<Array<String>>(key, default, title, description, required, validator) {
|
||||
companion object {
|
||||
/**
|
||||
* Create a new [StringArrayPatchOption] and add it to the current [Patch].
|
||||
*
|
||||
* @param key The identifier.
|
||||
* @param default The default value.
|
||||
* @param title The title.
|
||||
* @param description A description.
|
||||
* @param required Whether the option is required.
|
||||
* @return The created [StringArrayPatchOption].
|
||||
*
|
||||
* @see StringArrayPatchOption
|
||||
* @see PatchOption
|
||||
*/
|
||||
fun <T : Patch<*>> T.stringArrayPatchOption(
|
||||
key: String,
|
||||
default: Array<String>? = null,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
required: Boolean = false,
|
||||
validator: (Array<String>?) -> Boolean = { true }
|
||||
) = StringArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package app.revanced.patcher.issues
|
||||
|
||||
import app.revanced.patcher.patch.PatchOption
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertNull
|
||||
|
||||
internal class Issue98 {
|
||||
companion object {
|
||||
var key1: String? by PatchOption.StringOption(
|
||||
"key1", null, "title", "description"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should infer nullable type correctly`() {
|
||||
assertNull(key1)
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package app.revanced.patcher.patch
|
||||
|
||||
import app.revanced.patcher.usage.ExampleBytecodePatch
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
internal class PatchOptionsTest {
|
||||
private val options = ExampleBytecodePatch.options
|
||||
|
||||
@Test
|
||||
fun `should not throw an exception`() {
|
||||
for (option in options) {
|
||||
when (option) {
|
||||
is PatchOption.StringOption -> {
|
||||
option.value = "Hello World"
|
||||
}
|
||||
|
||||
is PatchOption.BooleanOption -> {
|
||||
option.value = false
|
||||
}
|
||||
|
||||
is PatchOption.StringListOption -> {
|
||||
option.value = option.options.first()
|
||||
for (choice in option.options) {
|
||||
assertNotNull(choice)
|
||||
}
|
||||
}
|
||||
|
||||
is PatchOption.IntListOption -> {
|
||||
option.value = option.options.first()
|
||||
for (choice in option.options) {
|
||||
assertNotNull(choice)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val option = options.get<String>("key1")
|
||||
// or: val option: String? by options["key1"]
|
||||
// then you won't need `.value` every time
|
||||
assertEquals("Hello World", option.value)
|
||||
options["key1"] = "Hello, world!"
|
||||
assertEquals("Hello, world!", option.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return a different value when changed`() {
|
||||
var value: String? by options["key1"]
|
||||
val current = value + "" // force a copy
|
||||
value = "Hello, world!"
|
||||
assertNotEquals(current, value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be able to set value to null`() {
|
||||
// Sadly, doing:
|
||||
// > options["key2"] = null
|
||||
// is not possible because Kotlin
|
||||
// cannot reify the type "Nothing?".
|
||||
// So we have to do this instead:
|
||||
options["key2"] = null as Any?
|
||||
// This is a cleaner replacement for the above:
|
||||
options.nullify("key2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because the option does not exist`() {
|
||||
assertThrows<NoSuchOptionException> {
|
||||
options["this option does not exist"] = 123
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because of invalid value type when setting an option`() {
|
||||
assertThrows<InvalidTypeException> {
|
||||
options["key1"] = 123
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because of invalid value type when getting an option`() {
|
||||
assertThrows<InvalidTypeException> {
|
||||
options.get<Int>("key1")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because of an illegal value`() {
|
||||
assertThrows<IllegalValueException> {
|
||||
options["key3"] = "this value is not an allowed option"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because the requirement is not met`() {
|
||||
assertThrows<RequirementNotMetException> {
|
||||
options.nullify("key1")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail because getting a non-initialized option is illegal`() {
|
||||
assertThrows<RequirementNotMetException> {
|
||||
options["key5"].value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package app.revanced.patcher.patch.options
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.options.types.BooleanPatchOption.Companion.booleanPatchOption
|
||||
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
||||
import app.revanced.patcher.patch.options.types.array.StringArrayPatchOption.Companion.stringArrayPatchOption
|
||||
import org.junit.jupiter.api.assertDoesNotThrow
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class PatchOptionsTest {
|
||||
@Test
|
||||
fun `should not fail because default value is unvalidated`() {
|
||||
assertDoesNotThrow {
|
||||
OptionsTestPatch.options["required"].value
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw due to incorrect type`() {
|
||||
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
||||
OptionsTestPatch.options["bool"] = 0
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be nullable`() {
|
||||
OptionsTestPatch.options["bool"] = null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `option should not be found`() {
|
||||
assertThrows<PatchOptionException.PatchOptionNotFoundException> {
|
||||
OptionsTestPatch.options["this option does not exist"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be able to add options manually`() {
|
||||
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
||||
OptionsTestPatch.options["array"] = OptionsTestPatch.stringArrayOption
|
||||
}
|
||||
assertDoesNotThrow {
|
||||
OptionsTestPatch.options.register(OptionsTestPatch.stringArrayOption)
|
||||
}
|
||||
}
|
||||
|
||||
private object OptionsTestPatch : BytecodePatch() {
|
||||
private var stringOption by stringPatchOption("string", "default")
|
||||
private var booleanOption by booleanPatchOption("bool", true)
|
||||
private var requiredStringOption by stringPatchOption("required", "default", required = true)
|
||||
private var nullDefaultRequiredOption by stringPatchOption("null", null, required = true)
|
||||
|
||||
val stringArrayOption = stringArrayPatchOption("array", arrayOf("1", "2"))
|
||||
|
||||
override fun execute(context: BytecodeContext) {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package app.revanced.patcher.patch.usage
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotations.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Format
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
||||
import com.android.tools.smali.dexlib2.util.Preconditions
|
||||
import com.google.common.collect.ImmutableList
|
||||
|
||||
@Suppress("unused")
|
||||
@Patch(
|
||||
name = "Example bytecode patch",
|
||||
description = "Example demonstration of a bytecode patch.",
|
||||
dependencies = [ExampleResourcePatch::class],
|
||||
compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))]
|
||||
)
|
||||
object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
||||
// Entry point of a patch. Supplied fingerprints are resolved at this point.
|
||||
override fun execute(context: BytecodeContext) {
|
||||
ExampleFingerprint.result?.let { result ->
|
||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
||||
// Get the start index of our opcode pattern.
|
||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
result.mutableMethod.apply {
|
||||
replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
||||
|
||||
// Store the fields initial value into the first virtual register.
|
||||
replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
||||
|
||||
// Now let's create a new call to our method and print the return value!
|
||||
// You can also use the smali compiler to create instructions.
|
||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
||||
//
|
||||
// Control flow instructions are not supported as of now.
|
||||
addInstructionsWithLabels(
|
||||
startIndex + 2,
|
||||
"""
|
||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// Find the class in which the method matching our fingerprint is defined in.
|
||||
context.findClass(result.classDef.type)!!.mutableClass.apply {
|
||||
// Add a new method that returns a string.
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
result.classDef.type,
|
||||
"returnHello",
|
||||
null,
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
null,
|
||||
null,
|
||||
ImmutableMethodImplementation(
|
||||
1,
|
||||
ImmutableList.of(
|
||||
BuilderInstruction21c(
|
||||
Opcode.CONST_STRING,
|
||||
0,
|
||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
||||
),
|
||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
||||
),
|
||||
null,
|
||||
null
|
||||
)
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
// Add a field in the main class.
|
||||
// We will use this field in our method below to call println on.
|
||||
// The field holds the Ljava/io/PrintStream->out; field.
|
||||
fields.add(
|
||||
ImmutableField(
|
||||
type,
|
||||
"dummyField",
|
||||
"Ljava/io/PrintStream;",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
ImmutableFieldEncodedValue(
|
||||
ImmutableFieldReference(
|
||||
"Ljava/lang/System;",
|
||||
"out",
|
||||
"Ljava/io/PrintStream;"
|
||||
)
|
||||
),
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
}
|
||||
} ?: throw PatchException("Fingerprint failed to resolve.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an existing instruction with a new one containing a reference to a new string.
|
||||
* @param index The index of the instruction to replace.
|
||||
* @param string The replacement string.
|
||||
*/
|
||||
private fun MutableMethod.replaceStringAt(index: Int, string: String) {
|
||||
val instruction = getInstruction(index)
|
||||
|
||||
// Utility method of dexlib2.
|
||||
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
||||
|
||||
// Cast this to an instruction of the format 21c.
|
||||
// The instruction format can be found in the docs at
|
||||
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
||||
val strInstruction = instruction as Instruction21c
|
||||
|
||||
// In our case we want an instruction with the opcode CONST_STRING
|
||||
// The format is 21c, so we create a new BuilderInstruction21c
|
||||
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
||||
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
||||
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
||||
replaceInstruction(
|
||||
index,
|
||||
"const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patcher.usage
|
||||
package app.revanced.patcher.patch.usage
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
@ -12,8 +12,8 @@ object ExampleFingerprint : MethodFingerprint(
|
|||
listOf("[L"),
|
||||
listOf(
|
||||
Opcode.SGET_OBJECT,
|
||||
null, // Testing unknown opcodes.
|
||||
Opcode.INVOKE_STATIC, // This is intentionally wrong to test the Fuzzy resolver.
|
||||
null, // Matching unknown opcodes.
|
||||
Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching.
|
||||
Opcode.RETURN_VOID
|
||||
),
|
||||
null
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patcher.usage
|
||||
package app.revanced.patcher.patch.usage
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
|
@ -1,193 +0,0 @@
|
|||
package app.revanced.patcher.usage
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchOption
|
||||
import app.revanced.patcher.patch.annotations.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Format
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
||||
import com.android.tools.smali.dexlib2.util.Preconditions
|
||||
import com.google.common.collect.ImmutableList
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@Suppress("unused")
|
||||
@Patch(
|
||||
name = "Example bytecode patch",
|
||||
description = "Example demonstration of a bytecode patch.",
|
||||
dependencies = [ExampleResourcePatch::class],
|
||||
compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))]
|
||||
)
|
||||
object ExampleBytecodePatch : BytecodePatch(
|
||||
setOf(ExampleFingerprint)
|
||||
) {
|
||||
|
||||
// This function will be executed by the patcher.
|
||||
// You can treat it as a constructor
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Get the resolved method by its fingerprint from the resolver cache
|
||||
val result = ExampleFingerprint.result!!
|
||||
|
||||
// Patch options
|
||||
assertNotNull(key1)
|
||||
key2 = false
|
||||
|
||||
// Get the implementation for the resolved method
|
||||
val method = result.mutableMethod
|
||||
val implementation = method.implementation!!
|
||||
|
||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
||||
// Get the start index of our opcode pattern.
|
||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
implementation.replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
||||
|
||||
// Get the class in which the method matching our fingerprint is defined in.
|
||||
val mainClass = context.findClass {
|
||||
it.type == result.classDef.type
|
||||
}!!.mutableClass
|
||||
|
||||
// Add a new method returning a string
|
||||
mainClass.methods.add(
|
||||
ImmutableMethod(
|
||||
result.classDef.type,
|
||||
"returnHello",
|
||||
null,
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
null,
|
||||
null,
|
||||
ImmutableMethodImplementation(
|
||||
1,
|
||||
ImmutableList.of(
|
||||
BuilderInstruction21c(
|
||||
Opcode.CONST_STRING,
|
||||
0,
|
||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
||||
),
|
||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
||||
),
|
||||
null,
|
||||
null
|
||||
)
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
// Add a field in the main class
|
||||
// We will use this field in our method below to call println on
|
||||
// The field holds the Ljava/io/PrintStream->out; field
|
||||
mainClass.fields.add(
|
||||
ImmutableField(
|
||||
mainClass.type,
|
||||
"dummyField",
|
||||
"Ljava/io/PrintStream;",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
ImmutableFieldEncodedValue(
|
||||
ImmutableFieldReference(
|
||||
"Ljava/lang/System;",
|
||||
"out",
|
||||
"Ljava/io/PrintStream;"
|
||||
)
|
||||
),
|
||||
null,
|
||||
null
|
||||
).toMutable()
|
||||
)
|
||||
|
||||
// store the fields initial value into the first virtual register
|
||||
method.replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
||||
|
||||
// Now let's create a new call to our method and print the return value!
|
||||
// You can also use the smali compiler to create instructions.
|
||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
||||
//
|
||||
// Control flow instructions are not supported as of now.
|
||||
method.addInstructionsWithLabels(
|
||||
startIndex + 2,
|
||||
"""
|
||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the string for an instruction at the given index with a new one.
|
||||
* @param index The index of the instruction to replace the string for
|
||||
* @param string The replacing string
|
||||
*/
|
||||
private fun MutableMethodImplementation.replaceStringAt(index: Int, string: String) {
|
||||
val instruction = this.instructions[index]
|
||||
|
||||
// Utility method of dexlib2
|
||||
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
||||
|
||||
// Cast this to an instruction of the format 21c
|
||||
// The instruction format can be found in the docs at
|
||||
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
||||
val strInstruction = instruction as Instruction21c
|
||||
|
||||
// In our case we want an instruction with the opcode CONST_STRING
|
||||
// The format is 21c, so we create a new BuilderInstruction21c
|
||||
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
||||
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
||||
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
||||
this.replaceInstruction(
|
||||
index,
|
||||
BuilderInstruction21c(
|
||||
Opcode.CONST_STRING,
|
||||
strInstruction.registerA,
|
||||
ImmutableStringReference(string)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private var key1 by option(
|
||||
PatchOption.StringOption(
|
||||
"key1", "default", "title", "description", true
|
||||
)
|
||||
)
|
||||
|
||||
private var key2 by option(
|
||||
PatchOption.BooleanOption(
|
||||
"key2", true, "title", "description" // required defaults to false
|
||||
)
|
||||
)
|
||||
|
||||
private var key3 by option(
|
||||
PatchOption.StringListOption(
|
||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
||||
)
|
||||
)
|
||||
|
||||
private var key4 by option(
|
||||
PatchOption.IntListOption(
|
||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
||||
)
|
||||
)
|
||||
|
||||
private var key5 by option(
|
||||
PatchOption.StringOption(
|
||||
"key5", null, "title", "description", true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in a new issue