You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-20 20:30:50 +02:00
Compare commits
31 Commits
v15.0.0-de
...
v15.0.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ff965e6953 | ||
![]() |
468d5d7421 | ||
![]() |
fc95b28c49 | ||
![]() |
69184187d9 | ||
![]() |
a802d0df46 | ||
![]() |
8de30633ae | ||
![]() |
a1fbb7990f | ||
![]() |
aa71146b1b | ||
![]() |
9fdb8f087f | ||
![]() |
670f0153de | ||
![]() |
1d7aeca696 | ||
![]() |
4e7811ea07 | ||
![]() |
e11283744a | ||
![]() |
91cdfd53ef | ||
![]() |
bc7d6b9941 | ||
![]() |
6b1e0a1656 | ||
![]() |
72c9eb2129 | ||
![]() |
4bc4b0dc01 | ||
![]() |
637d48746f | ||
![]() |
9a109c129b | ||
![]() |
d49e4ee5ea | ||
![]() |
30f0ea29a3 | ||
![]() |
49930f6565 | ||
![]() |
909d89fa8d | ||
![]() |
81d1d7f544 | ||
![]() |
67b7dff67a | ||
![]() |
4b76d19596 | ||
![]() |
080fbe9feb | ||
![]() |
d3721229bf | ||
![]() |
86c1c9c772 | ||
![]() |
c299817193 |
93
CHANGELOG.md
93
CHANGELOG.md
@@ -1,3 +1,96 @@
|
|||||||
|
# [15.0.0](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0) (2023-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
||||||
|
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
||||||
|
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
||||||
|
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
||||||
|
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
||||||
|
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
||||||
|
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
||||||
|
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
||||||
|
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
||||||
|
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
||||||
|
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
||||||
|
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
||||||
|
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
||||||
|
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
||||||
|
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
||||||
|
|
||||||
|
|
||||||
|
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
||||||
|
* feat!: Add patch annotation processor ([3fc6a13](https://github.com/ReVanced/revanced-patcher/commit/3fc6a139eef67237c116fb4e3e29bf9542d3a981))
|
||||||
|
* feat!: Remove patch annotations ([3b4db3d](https://github.com/ReVanced/revanced-patcher/commit/3b4db3ddb72cdcee8af2f787eadf58eeb37543de))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add patch annotation processor ([#231](https://github.com/ReVanced/revanced-patcher/issues/231)) ([a29931f](https://github.com/ReVanced/revanced-patcher/commit/a29931f2ec0a666dba209b54855425d9dc2f4462))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This gets rid of the public constructor.
|
||||||
|
* `PatchBundleLoader` is not a map anymore
|
||||||
|
* This renames packages and the Maven package.
|
||||||
|
* The manifest for patches has been removed, and the properties have been added to patches. Patches are now `OptionsContainer`. The `@Patch` annotation has been removed in favour of the `@Patch` annotation from the annotation processor.
|
||||||
|
* Patch annotations have been removed. PatcherException is now thrown in various places. PatchBundleLoader is now a map of patches associated by their name. Patches are now instances.
|
||||||
|
|
||||||
|
# [15.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.3...v15.0.0-dev.4) (2023-09-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
||||||
|
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
||||||
|
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
||||||
|
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
||||||
|
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
||||||
|
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
||||||
|
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
||||||
|
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
||||||
|
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
||||||
|
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
||||||
|
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
||||||
|
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
||||||
|
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This gets rid of the public constructor.
|
||||||
|
|
||||||
|
# [15.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.2...v15.0.0-dev.3) (2023-09-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
||||||
|
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
||||||
|
|
||||||
|
|
||||||
|
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* `PatchBundleLoader` is not a map anymore
|
||||||
|
* This renames packages and the Maven package.
|
||||||
|
|
||||||
|
# [15.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.1...v15.0.0-dev.2) (2023-09-06)
|
||||||
|
|
||||||
# [15.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0-dev.1) (2023-09-04)
|
# [15.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0-dev.1) (2023-09-04)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -2,3 +2,9 @@ plugins {
|
|||||||
kotlin("jvm") version "1.9.0" apply false
|
kotlin("jvm") version "1.9.0" apply false
|
||||||
alias(libs.plugins.binary.compatibility.validator)
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply(plugin = "maven-publish")
|
||||||
|
|
||||||
|
group = "app.revanced"
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 15.0.0-dev.1
|
version = 15.0.0
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
public abstract interface annotation class app/revanced/patcher/patch/annotations/CompatiblePackage : java/lang/annotation/Annotation {
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/CompatiblePackage : java/lang/annotation/Annotation {
|
||||||
public abstract fun name ()Ljava/lang/String;
|
public abstract fun name ()Ljava/lang/String;
|
||||||
public abstract fun versions ()[Ljava/lang/String;
|
public abstract fun versions ()[Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract interface annotation class app/revanced/patcher/patch/annotations/Patch : java/lang/annotation/Annotation {
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/Patch : java/lang/annotation/Annotation {
|
||||||
public abstract fun compatiblePackages ()[Lapp/revanced/patcher/patch/annotations/CompatiblePackage;
|
public abstract fun compatiblePackages ()[Lapp/revanced/patcher/patch/annotation/CompatiblePackage;
|
||||||
public abstract fun dependencies ()[Ljava/lang/Class;
|
public abstract fun dependencies ()[Ljava/lang/Class;
|
||||||
public abstract fun description ()Ljava/lang/String;
|
public abstract fun description ()Ljava/lang/String;
|
||||||
public abstract fun name ()Ljava/lang/String;
|
public abstract fun name ()Ljava/lang/String;
|
||||||
@@ -12,14 +12,13 @@ public abstract interface annotation class app/revanced/patcher/patch/annotation
|
|||||||
public abstract fun use ()Z
|
public abstract fun use ()Z
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/annotations/processor/PatchProcessor : com/google/devtools/ksp/processing/SymbolProcessor {
|
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessor : com/google/devtools/ksp/processing/SymbolProcessor {
|
||||||
public fun <init> (Lcom/google/devtools/ksp/processing/CodeGenerator;Lcom/google/devtools/ksp/processing/KSPLogger;)V
|
|
||||||
public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
|
public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/annotations/processor/PatchProcessorProvider : com/google/devtools/ksp/processing/SymbolProcessorProvider {
|
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessorProvider : com/google/devtools/ksp/processing/SymbolProcessorProvider {
|
||||||
public fun <init> ()V
|
public fun <init> ()V
|
||||||
public fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lapp/revanced/patcher/patch/annotations/processor/PatchProcessor;
|
public fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lapp/revanced/patcher/patch/annotation/processor/PatchProcessor;
|
||||||
public synthetic fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lcom/google/devtools/ksp/processing/SymbolProcessor;
|
public synthetic fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lcom/google/devtools/ksp/processing/SymbolProcessor;
|
||||||
}
|
}
|
||||||
|
|
74
revanced-patch-annotation-processor/build.gradle.kts
Normal file
74
revanced-patch-annotation-processor/build.gradle.kts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.9.0"
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.symbol.processing.api)
|
||||||
|
implementation(libs.kotlinpoet.ksp)
|
||||||
|
implementation(project(":revanced-patcher"))
|
||||||
|
|
||||||
|
testImplementation(libs.kotlin.test)
|
||||||
|
testImplementation(libs.kotlin.compile.testing)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin { jvmToolchain(11) }
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("gpr") {
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
version = project.version.toString()
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "ReVanced patch annotation processor"
|
||||||
|
description = "Annotation processor for patches."
|
||||||
|
url = "https://revanced.app"
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = "GNU General Public License v3.0"
|
||||||
|
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "ReVanced"
|
||||||
|
name = "ReVanced"
|
||||||
|
email = "contact@revanced.app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
||||||
|
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
||||||
|
url = "https://github.com/revanced/revanced-patcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
revanced-patch-annotation-processor/settings.gradle.kts
Normal file
2
revanced-patch-annotation-processor/settings.gradle.kts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = "revanced-patch-annotation-processor"
|
||||||
|
|
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.patcher.patch.annotations
|
package app.revanced.patcher.patch.annotation
|
||||||
|
|
||||||
import java.lang.annotation.Inherited
|
import java.lang.annotation.Inherited
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
@@ -1,12 +1,14 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import com.google.devtools.ksp.processing.*
|
import com.google.devtools.ksp.processing.CodeGenerator
|
||||||
|
import com.google.devtools.ksp.processing.Dependencies
|
||||||
|
import com.google.devtools.ksp.processing.Resolver
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||||
import com.google.devtools.ksp.symbol.KSAnnotation
|
import com.google.devtools.ksp.symbol.KSAnnotation
|
||||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||||
@@ -17,9 +19,8 @@ import com.squareup.kotlinpoet.ksp.toClassName
|
|||||||
import com.squareup.kotlinpoet.ksp.writeTo
|
import com.squareup.kotlinpoet.ksp.writeTo
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class PatchProcessor(
|
class PatchProcessor internal constructor(
|
||||||
private val codeGenerator: CodeGenerator,
|
private val codeGenerator: CodeGenerator,
|
||||||
private val logger: KSPLogger
|
|
||||||
) : SymbolProcessor {
|
) : SymbolProcessor {
|
||||||
|
|
||||||
private fun KSAnnotated.isSubclassOf(cls: KClass<*>): Boolean {
|
private fun KSAnnotated.isSubclassOf(cls: KClass<*>): Boolean {
|
||||||
@@ -32,7 +33,7 @@ class PatchProcessor(
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||||
val executablePatches = buildMap {
|
val patches = buildMap {
|
||||||
resolver.getSymbolsWithAnnotation(Patch::class.qualifiedName!!).filter {
|
resolver.getSymbolsWithAnnotation(Patch::class.qualifiedName!!).filter {
|
||||||
// Do not check here if Patch is super of the class, because it is expensive.
|
// Do not check here if Patch is super of the class, because it is expensive.
|
||||||
// Check it later when processing.
|
// Check it later when processing.
|
||||||
@@ -80,14 +81,14 @@ class PatchProcessor(
|
|||||||
dependencies?.map { dependency -> dependency.toClassName() },
|
dependencies?.map { dependency -> dependency.toClassName() },
|
||||||
compatiblePackages?.map {
|
compatiblePackages?.map {
|
||||||
val packageName = it.property("name")
|
val packageName = it.property("name")
|
||||||
val packageVersions = (it.property("versions") as List<String>)
|
val packageVersions = (it.property("versions") as List<String>).ifEmpty { null }
|
||||||
.joinToString(", ") { version -> "\"$version\"" }
|
?.joinToString(", ") { version -> "\"$version\"" }
|
||||||
|
|
||||||
CodeBlock.of(
|
CodeBlock.of(
|
||||||
"%T(%S, setOf(%L))",
|
"%T(%S, %L)",
|
||||||
app.revanced.patcher.patch.Patch.CompatiblePackage::class,
|
app.revanced.patcher.patch.Patch.CompatiblePackage::class,
|
||||||
packageName,
|
packageName,
|
||||||
packageVersions
|
packageVersions?.let { "setOf($packageVersions)" },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
use,
|
use,
|
||||||
@@ -98,13 +99,13 @@ class PatchProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If a patch depends on another, that is annotated, the dependency should be replaced with the generated patch,
|
// If a patch depends on another, that is annotated, the dependency should be replaced with the generated patch,
|
||||||
// because the generated patch has all the necessary properties to invoke the super constructor,
|
// because the generated patch has all the necessary properties to invoke the super constructor with,
|
||||||
// unlike the annotated patch.
|
// unlike the annotated patch.
|
||||||
val dependencyResolutionMap = buildMap {
|
val dependencyResolutionMap = buildMap {
|
||||||
executablePatches.values.filter { it.dependencies != null }.flatMap {
|
patches.values.filter { it.dependencies != null }.flatMap {
|
||||||
it.dependencies!!
|
it.dependencies!!
|
||||||
}.distinct().forEach { dependency ->
|
}.distinct().forEach { dependency ->
|
||||||
executablePatches.keys.find { it.qualifiedName?.asString() == dependency.toString() }
|
patches.keys.find { it.qualifiedName?.asString() == dependency.toString() }
|
||||||
?.let { patch ->
|
?.let { patch ->
|
||||||
this[dependency] = ClassName(
|
this[dependency] = ClassName(
|
||||||
patch.packageName.asString(),
|
patch.packageName.asString(),
|
||||||
@@ -114,8 +115,7 @@ class PatchProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// kotlin poet generate a class for each patch
|
patches.forEach { (patchDeclaration, patchAnnotation) ->
|
||||||
executablePatches.forEach { (patchDeclaration, patchAnnotation) ->
|
|
||||||
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
||||||
|
|
||||||
val superClass = if (isBytecodePatch) {
|
val superClass = if (isBytecodePatch) {
|
||||||
@@ -154,18 +154,18 @@ class PatchProcessor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
patchAnnotation.dependencies?.let { dependencies ->
|
// The generated patch always depends on the source patch.
|
||||||
addSuperclassConstructorParameter(
|
addSuperclassConstructorParameter(
|
||||||
"dependencies = setOf(%L)",
|
"dependencies = setOf(%L)",
|
||||||
buildList {
|
buildList {
|
||||||
addAll(dependencies)
|
patchAnnotation.dependencies?.forEach { dependency ->
|
||||||
// Also add the source class of the generated class so that it is also executed
|
add("${(dependencyResolutionMap[dependency] ?: dependency)}::class")
|
||||||
add(patchDeclaration.toClassName())
|
|
||||||
}.joinToString(", ") { dependency ->
|
|
||||||
"${(dependencyResolutionMap[dependency] ?: dependency)}::class"
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
add("${patchDeclaration.toClassName()}::class")
|
||||||
|
}.joinToString(", "),
|
||||||
|
)
|
||||||
|
|
||||||
addSuperclassConstructorParameter(
|
addSuperclassConstructorParameter(
|
||||||
"use = %L", patchAnnotation.use
|
"use = %L", patchAnnotation.use
|
||||||
)
|
)
|
||||||
@@ -181,9 +181,18 @@ class PatchProcessor(
|
|||||||
.addParameter("context", contextClass)
|
.addParameter("context", contextClass)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.addProperty(
|
.addInitializerBlock(
|
||||||
PropertySpec.builder("options", PatchOptions::class, KModifier.OVERRIDE)
|
CodeBlock.builder()
|
||||||
.initializer("%T.options", patchDeclaration.toClassName())
|
.add(
|
||||||
|
"%T.options.forEach { (_, option) ->",
|
||||||
|
patchDeclaration.toClassName()
|
||||||
|
)
|
||||||
|
.addStatement(
|
||||||
|
"options.register(option)"
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
"}"
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
@@ -1,9 +1,9 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||||
|
|
||||||
class PatchProcessorProvider : SymbolProcessorProvider {
|
class PatchProcessorProvider : SymbolProcessorProvider {
|
||||||
override fun create(environment: SymbolProcessorEnvironment) =
|
override fun create(environment: SymbolProcessorEnvironment) =
|
||||||
PatchProcessor(environment.codeGenerator, environment.logger)
|
PatchProcessor(environment.codeGenerator)
|
||||||
}
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
app.revanced.patcher.patch.annotation.processor.PatchProcessorProvider
|
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import com.tschuchort.compiletesting.KotlinCompilation
|
import com.tschuchort.compiletesting.KotlinCompilation
|
||||||
@@ -7,7 +7,6 @@ import com.tschuchort.compiletesting.kspWithCompilation
|
|||||||
import com.tschuchort.compiletesting.symbolProcessorProviders
|
import com.tschuchort.compiletesting.symbolProcessorProviders
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class TestPatchAnnotationProcessor {
|
class TestPatchAnnotationProcessor {
|
||||||
@@ -22,6 +21,16 @@ class TestPatchAnnotationProcessor {
|
|||||||
).loadPatch("$SAMPLE_PACKAGE.processing.ProcessablePatchGenerated").name
|
).loadPatch("$SAMPLE_PACKAGE.processing.ProcessablePatchGenerated").name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateNullProperties() = compile(
|
||||||
|
getSourceFile(
|
||||||
|
"null", "NullPropertiesPatch"
|
||||||
|
)
|
||||||
|
).loadPatch("$SAMPLE_PACKAGE.null.NullPropertiesPatchGenerated").let {
|
||||||
|
assertNull(it.description) // Because no description was provided.
|
||||||
|
assertNull(it.compatiblePackages!!.first().versions) // Because no versions were provided.
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Dependencies
|
// region Dependencies
|
||||||
@@ -64,28 +73,30 @@ class TestPatchAnnotationProcessor {
|
|||||||
)
|
)
|
||||||
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
||||||
|
|
||||||
assertNotNull(patch.options)
|
assert(patch.options.isNotEmpty())
|
||||||
assertEquals(patch.options["print"].title, "Print message")
|
assertEquals(patch.options["print"].title, "Print message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Limitations
|
// region Limitations
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun failingManualDependency() = assertNull(
|
fun failingManualDependency() = assertEquals(
|
||||||
|
1, // Generated patch is always dependent on source class.
|
||||||
compile(
|
compile(
|
||||||
getSourceFile(
|
getSourceFile(
|
||||||
"limitations/manualdependency", "DependentPatch"
|
"limitations/manualdependency", "DependentPatch"
|
||||||
), getSourceFile(
|
), getSourceFile(
|
||||||
"limitations/manualdependency", "DependencyPatch"
|
"limitations/manualdependency", "DependencyPatch"
|
||||||
)
|
)
|
||||||
).loadPatch("$SAMPLE_PACKAGE.limitations.manualdependency.DependentPatchGenerated").dependencies
|
).loadPatch("$SAMPLE_PACKAGE.limitations.manualdependency.DependentPatchGenerated").dependencies!!.size
|
||||||
)
|
)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
private companion object Utils {
|
private companion object Utils {
|
||||||
const val SAMPLE_PACKAGE = "app.revanced.patcher.patch.annotations.processor.samples"
|
const val SAMPLE_PACKAGE = "app.revanced.patcher.patch.annotation.processor.samples"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a source file from the given sample and class name.
|
* Get a source file from the given sample and class name.
|
||||||
@@ -94,11 +105,16 @@ class TestPatchAnnotationProcessor {
|
|||||||
* @param className The name of the class to get the source file from.
|
* @param className The name of the class to get the source file from.
|
||||||
* @return The source file.
|
* @return The source file.
|
||||||
*/
|
*/
|
||||||
fun getSourceFile(sample: String, className: String) = SourceFile.kotlin(
|
fun getSourceFile(sample: String, className: String): SourceFile {
|
||||||
"$className.kt", TestPatchAnnotationProcessor::class.java.classLoader.getResourceAsStream(
|
val resourceName = "app/revanced/patcher/patch/annotation/processor/samples/$sample/$className.kt"
|
||||||
"app/revanced/patcher/patch/annotations/processor/samples/$sample/$className.kt"
|
return SourceFile.kotlin(
|
||||||
)?.readAllBytes()?.toString(Charsets.UTF_8) ?: error("Could not find resource $className")
|
"$className.kt",
|
||||||
)
|
TestPatchAnnotationProcessor::class.java.classLoader.getResourceAsStream(resourceName)
|
||||||
|
?.readAllBytes()
|
||||||
|
?.toString(Charsets.UTF_8)
|
||||||
|
?: error("Could not find resource $resourceName")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile the given source files and return the result.
|
* Compile the given source files and return the result.
|
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor.samples.dependencies
|
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
@Patch(name = "Dependency patch")
|
@Patch(name = "Dependency patch")
|
||||||
object DependencyPatch : ResourcePatch() {
|
object DependencyPatch : ResourcePatch() {
|
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor.samples.dependencies
|
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Dependent patch",
|
name = "Dependent patch",
|
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor.samples.limitations.manualdependency
|
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
@Patch(name = "Dependency patch")
|
@Patch(name = "Dependency patch")
|
||||||
object DependencyPatch : ResourcePatch() {
|
object DependencyPatch : ResourcePatch() {
|
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor.samples.limitations.manualdependency
|
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
@Patch(name = "Dependent patch")
|
@Patch(name = "Dependent patch")
|
||||||
object DependentPatch : BytecodePatch(
|
object DependentPatch : BytecodePatch(
|
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.`null`
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
"Patch with null properties",
|
||||||
|
compatiblePackages = [CompatiblePackage("com.google.android.youtube")],
|
||||||
|
)
|
||||||
|
object NullPropertiesPatch : BytecodePatch() {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.options
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.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 stringPatchOption(
|
||||||
|
"print",
|
||||||
|
null,
|
||||||
|
"Print message",
|
||||||
|
"The message to print."
|
||||||
|
)
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.patch.annotations.processor.samples.processing
|
package app.revanced.patcher.patch.annotation.processor.samples.processing
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
@Patch("Processable patch")
|
@Patch("Processable patch")
|
||||||
object ProcessablePatch : BytecodePatch() {
|
object ProcessablePatch : BytecodePatch() {
|
@@ -1,50 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm") version "1.9.0"
|
|
||||||
`maven-publish`
|
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.symbol.processing.api)
|
|
||||||
implementation(libs.kotlinpoet.ksp)
|
|
||||||
implementation(project(":revanced-patcher"))
|
|
||||||
|
|
||||||
testImplementation(libs.kotlin.test)
|
|
||||||
testImplementation(libs.kotlin.compile.testing)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin { jvmToolchain(11) }
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
maven {
|
|
||||||
name = "GitHubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patch-annotations-processor")
|
|
||||||
credentials {
|
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
publications {
|
|
||||||
create<MavenPublication>("gpr") {
|
|
||||||
from(components["java"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,2 +0,0 @@
|
|||||||
rootProject.name = "revanced-patch-annotations-processor"
|
|
||||||
|
|
@@ -1 +0,0 @@
|
|||||||
app.revanced.patcher.patch.annotations.processor.PatchProcessorProvider
|
|
@@ -1,21 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
@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."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.0"
|
kotlin("jvm") version "1.9.0"
|
||||||
`maven-publish`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
implementation(libs.xpp3)
|
implementation(libs.xpp3)
|
||||||
@@ -15,7 +12,7 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly(libs.android)
|
compileOnly(libs.android)
|
||||||
|
|
||||||
testImplementation(project(":revanced-patch-annotations-processor"))
|
testImplementation(project(":revanced-patch-annotation-processor"))
|
||||||
testImplementation(libs.kotlin.test)
|
testImplementation(libs.kotlin.test)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +50,33 @@ publishing {
|
|||||||
publications {
|
publications {
|
||||||
create<MavenPublication>("gpr") {
|
create<MavenPublication>("gpr") {
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
|
|
||||||
|
version = project.version.toString()
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "ReVanced Patcher"
|
||||||
|
description = "Patcher used by ReVanced."
|
||||||
|
url = "https://revanced.app"
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = "GNU General Public License v3.0"
|
||||||
|
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "ReVanced"
|
||||||
|
name = "ReVanced"
|
||||||
|
email = "contact@revanced.app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
||||||
|
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
||||||
|
url = "https://github.com/revanced/revanced-patcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,9 @@ import java.util.logging.Logger
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Patch]es mapped by their name.
|
* A set of [Patch]es.
|
||||||
*/
|
*/
|
||||||
typealias PatchMap = Map<String, Patch<*>>
|
typealias PatchSet = Set<Patch<*>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Patch] class.
|
* A [Patch] class.
|
||||||
@@ -28,28 +28,30 @@ typealias PatchClass = KClass<out Patch<*>>
|
|||||||
*
|
*
|
||||||
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
||||||
* @param classLoader The [ClassLoader] to use for loading the classes.
|
* @param classLoader The [ClassLoader] to use for loading the classes.
|
||||||
|
* @param patchBundles A set of patches to initialize this instance with.
|
||||||
*/
|
*/
|
||||||
sealed class PatchBundleLoader private constructor(
|
sealed class PatchBundleLoader private constructor(
|
||||||
classLoader: ClassLoader,
|
classLoader: ClassLoader,
|
||||||
patchBundles: Array<out File>,
|
patchBundles: Array<out File>,
|
||||||
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
||||||
) : PatchMap by mutableMapOf() {
|
// This constructor parameter is unfortunately necessary,
|
||||||
|
// so that a reference to the mutable set is present in the constructor to be able to add patches to it.
|
||||||
|
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
||||||
|
private val patchSet: MutableSet<Patch<*>> = mutableSetOf()
|
||||||
|
) : PatchSet by patchSet {
|
||||||
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
||||||
classLoader.loadClass(it)
|
classLoader.loadClass(it)
|
||||||
}.filter {
|
}.filter {
|
||||||
it.isInstance(Patch::class.java)
|
Patch::class.java.isAssignableFrom(it)
|
||||||
}.mapNotNull { patchClass ->
|
}.mapNotNull { patchClass ->
|
||||||
patchClass.getInstance(logger)
|
patchClass.getInstance(logger, silent = true)
|
||||||
}.filter {
|
}.filter {
|
||||||
it.name != null
|
it.name != null
|
||||||
}.associateBy {
|
|
||||||
it.name!!
|
|
||||||
}.let { patches ->
|
}.let { patches ->
|
||||||
@Suppress("UNCHECKED_CAST")
|
patchSet.addAll(patches)
|
||||||
(this as MutableMap<String, Patch<*>>).putAll(patches)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +60,14 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
||||||
*
|
*
|
||||||
* @param logger The [Logger] to use for logging.
|
* @param logger The [Logger] to use for logging.
|
||||||
|
* @param silent Whether to suppress logging.
|
||||||
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
||||||
*/
|
*/
|
||||||
internal fun Class<*>.getInstance(logger: Logger): Patch<*>? {
|
internal fun Class<*>.getInstance(logger: Logger, silent: Boolean = false): Patch<*>? {
|
||||||
return try {
|
return try {
|
||||||
getField("INSTANCE").get(null)
|
getField("INSTANCE").get(null)
|
||||||
} catch (exception: NoSuchFileException) {
|
} catch (exception: NoSuchFieldException) {
|
||||||
logger.fine(
|
if (!silent) logger.fine(
|
||||||
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
||||||
"Will try to instantiate it."
|
"Will try to instantiate it."
|
||||||
)
|
)
|
||||||
@@ -72,7 +75,7 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
try {
|
try {
|
||||||
getDeclaredConstructor().newInstance()
|
getDeclaredConstructor().newInstance()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
logger.severe(
|
if (!silent) logger.severe(
|
||||||
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
||||||
"therefor cannot be instantiated and will be ignored."
|
"therefor cannot be instantiated and will be ignored."
|
||||||
)
|
)
|
||||||
|
@@ -74,7 +74,7 @@ class Patcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add all patches and their dependencies to the context.
|
// Add all patches and their dependencies to the context.
|
||||||
for (patch in patches) context.executablePatches.putIfAbsent(patch::class, patch) ?: {
|
for (patch in patches) context.executablePatches.putIfAbsent(patch::class, patch) ?: run {
|
||||||
context.allPatches[patch::class] = patch
|
context.allPatches[patch::class] = patch
|
||||||
|
|
||||||
patch.dependencies?.forEach { it.putDependenciesRecursively() }
|
patch.dependencies?.forEach { it.putDependenciesRecursively() }
|
||||||
@@ -153,7 +153,7 @@ class Patcher(
|
|||||||
patch: Patch<*>,
|
patch: Patch<*>,
|
||||||
executedPatches: LinkedHashMap<Patch<*>, PatchResult>
|
executedPatches: LinkedHashMap<Patch<*>, PatchResult>
|
||||||
): PatchResult {
|
): PatchResult {
|
||||||
val patchName = patch.name
|
val patchName = patch.name ?: patch.toString()
|
||||||
|
|
||||||
executedPatches[patch]?.let { patchResult ->
|
executedPatches[patch]?.let { patchResult ->
|
||||||
patchResult.exception ?: return patchResult
|
patchResult.exception ?: return patchResult
|
||||||
@@ -164,14 +164,17 @@ class Patcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recursively execute all dependency patches.
|
// Recursively execute all dependency patches.
|
||||||
patch.dependencies?.forEach { dependencyName ->
|
patch.dependencies?.forEach { dependencyClass ->
|
||||||
val dependency = context.executablePatches[dependencyName]!!
|
val dependency = context.allPatches[dependencyClass]!!
|
||||||
val result = executePatch(dependency, executedPatches)
|
val result = executePatch(dependency, executedPatches)
|
||||||
|
|
||||||
result.exception?.let {
|
result.exception?.let {
|
||||||
return PatchResult(
|
return PatchResult(
|
||||||
patch,
|
patch,
|
||||||
PatchException("'$patchName' depends on '${dependency}' that raised an exception: $it")
|
PatchException(
|
||||||
|
"'$patchName' depends on '${dependency.name ?: dependency}' " +
|
||||||
|
"that raised an exception:\n${it.stackTraceToString()}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +183,7 @@ class Patcher(
|
|||||||
// TODO: Implement this in a more polymorphic way.
|
// TODO: Implement this in a more polymorphic way.
|
||||||
when (patch) {
|
when (patch) {
|
||||||
is BytecodePatch -> {
|
is BytecodePatch -> {
|
||||||
patch.fingerprints.toList().resolveUsingLookupMap(context.bytecodeContext)
|
patch.fingerprints.resolveUsingLookupMap(context.bytecodeContext)
|
||||||
patch.execute(context.bytecodeContext)
|
patch.execute(context.bytecodeContext)
|
||||||
}
|
}
|
||||||
is ResourcePatch -> {
|
is ResourcePatch -> {
|
||||||
@@ -208,7 +211,7 @@ class Patcher(
|
|||||||
|
|
||||||
val executedPatches = LinkedHashMap<Patch<*>, PatchResult>() // Key is name.
|
val executedPatches = LinkedHashMap<Patch<*>, PatchResult>() // Key is name.
|
||||||
|
|
||||||
context.executablePatches.map { it.value }.sortedBy { it.name }.forEach { patch ->
|
context.executablePatches.values.sortedBy { it.name }.forEach { patch ->
|
||||||
val patchResult = executePatch(patch, executedPatches)
|
val patchResult = executePatch(patch, executedPatches)
|
||||||
|
|
||||||
// If the patch failed, emit the result, even if it is closeable.
|
// If the patch failed, emit the result, even if it is closeable.
|
||||||
@@ -245,7 +248,7 @@ class Patcher(
|
|||||||
PatchResult(
|
PatchResult(
|
||||||
patch,
|
patch,
|
||||||
PatchException(
|
PatchException(
|
||||||
"'${patch.name}' raised an exception while being closed: $it",
|
"'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}",
|
||||||
result.exception
|
result.exception
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@@ -159,9 +159,12 @@ abstract class MethodFingerprint(
|
|||||||
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
*/
|
*/
|
||||||
internal fun List<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
internal fun Set<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
||||||
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
||||||
|
|
||||||
|
forEach { fingerprint ->
|
||||||
|
fingerprint.resolveUsingLookupMap(context)
|
||||||
|
}
|
||||||
for (fingerprint in this) {
|
for (fingerprint in this) {
|
||||||
fingerprint.resolveUsingLookupMap(context)
|
fingerprint.resolveUsingLookupMap(context)
|
||||||
}
|
}
|
||||||
|
@@ -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.PatchClass
|
||||||
import app.revanced.patcher.Patcher
|
import app.revanced.patcher.Patcher
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.data.Context
|
import app.revanced.patcher.data.Context
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.patch.options.PatchOptions
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +30,12 @@ sealed class Patch<out T : Context<*>>(
|
|||||||
val use: Boolean = true,
|
val use: Boolean = true,
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
val requiresIntegrations: Boolean = false,
|
val requiresIntegrations: Boolean = false,
|
||||||
) : OptionsContainer() {
|
) {
|
||||||
|
/**
|
||||||
|
* The options of the patch associated by the options key.
|
||||||
|
*/
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The execution function of the patch.
|
* The execution function of the patch.
|
||||||
*
|
*
|
||||||
@@ -62,48 +65,7 @@ sealed class Patch<out T : Context<*>>(
|
|||||||
*/
|
*/
|
||||||
class CompatiblePackage(
|
class CompatiblePackage(
|
||||||
val name: String,
|
val name: String,
|
||||||
versions: Set<String>? = null,
|
val versions: Set<String>? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
* @param exception The [PatchException] thrown, if any.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) {
|
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
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user