1
mirror of https://github.com/revanced/revanced-patcher synced 2025-09-13 18:30:49 +02:00

Compare commits

..

30 Commits

Author SHA1 Message Date
semantic-release-bot
8f78f85e4a chore(release): 4.2.2 [skip ci]
## [4.2.2](https://github.com/revanced/revanced-patcher/compare/v4.2.1...v4.2.2) (2022-09-08)

### Bug Fixes

* invalid type propagation in options ([b873228](b873228ef0)), closes [#98](https://github.com/revanced/revanced-patcher/issues/98)
2022-09-08 14:50:38 +00:00
Sculas
0be2677519 Merge remote-tracking branch 'origin/main' into main 2022-09-08 16:49:26 +02:00
Sculas
b873228ef0 fix: invalid type propagation in options
Fixes #98
2022-09-08 16:49:06 +02:00
semantic-release-bot
639ff1c0ba chore(release): 4.2.1 [skip ci]
## [4.2.1](https://github.com/revanced/revanced-patcher/compare/v4.2.0...v4.2.1) (2022-09-08)

### Bug Fixes

* make patcher version public ([76c45dd](76c45dd7c1))
2022-09-08 12:51:09 +00:00
Sculas
f30671ddd1 Merge remote-tracking branch 'origin/main' into main 2022-09-08 14:49:35 +02:00
Sculas
76c45dd7c1 fix: make patcher version public 2022-09-08 14:49:26 +02:00
semantic-release-bot
1bafb77355 chore(release): 4.2.0 [skip ci]
# [4.2.0](https://github.com/revanced/revanced-patcher/compare/v4.1.5...v4.2.0) (2022-09-08)

### Bug Fixes

* remove repeatable from PatchDeprecated ([6e73631](6e73631d4d))

### Features

* SincePatcher annotation ([25f74dc](25f74dc5e9))
2022-09-08 12:43:11 +00:00
Sculas
25f74dc5e9 feat: SincePatcher annotation 2022-09-08 14:41:42 +02:00
Sculas
6e73631d4d fix: remove repeatable from PatchDeprecated 2022-09-08 14:00:15 +02:00
semantic-release-bot
7761d5b85e chore(release): 4.1.5 [skip ci]
## [4.1.5](https://github.com/revanced/revanced-patcher/compare/v4.1.4...v4.1.5) (2022-09-08)

### Bug Fixes

* broken deprecation message ([62aa295](62aa295e73))
2022-09-08 11:43:39 +00:00
Sculas
62aa295e73 fix: broken deprecation message 2022-09-08 13:42:26 +02:00
Sculas
596ede1b12 refactor: make patchName work on any class 2022-09-08 13:42:15 +02:00
semantic-release-bot
7debe62738 chore(release): 4.1.4 [skip ci]
## [4.1.4](https://github.com/revanced/revanced-patcher/compare/v4.1.3...v4.1.4) (2022-09-08)

### Bug Fixes

* handle option types and nulls properly ([aff4968](aff4968e6f))
2022-09-08 09:30:49 +00:00
Sculas
002f84da1a Merge remote-tracking branch 'origin/main' into main 2022-09-08 11:29:14 +02:00
Sculas
aff4968e6f fix: handle option types and nulls properly 2022-09-08 11:29:06 +02:00
Sculas
1d989abd55 chore: ignore kotlinc 2022-09-08 11:07:34 +02:00
semantic-release-bot
f1775f83d0 chore(release): 4.1.3 [skip ci]
## [4.1.3](https://github.com/revanced/revanced-patcher/compare/v4.1.2...v4.1.3) (2022-09-07)

### Bug Fixes

* only run list option check if not null ([4055939](4055939c08))
2022-09-07 21:48:24 +00:00
Sculas
4055939c08 fix: only run list option check if not null 2022-09-07 23:46:46 +02:00
semantic-release-bot
85120374d6 chore(release): 4.1.2 [skip ci]
## [4.1.2](https://github.com/revanced/revanced-patcher/compare/v4.1.1...v4.1.2) (2022-09-07)

### Bug Fixes

* invalid types for example options ([79f91e0](79f91e0e5a))
2022-09-07 21:24:50 +00:00
Sculas
8b4819faa1 Merge remote-tracking branch 'origin/main' into main 2022-09-07 23:23:29 +02:00
semantic-release-bot
d219276298 chore(release): 4.1.1 [skip ci]
## [4.1.1](https://github.com/revanced/revanced-patcher/compare/v4.1.0...v4.1.1) (2022-09-07)

### Bug Fixes

* handle private companion objects ([ad3d332](ad3d332e27))
2022-09-07 21:23:12 +00:00
Sculas
79f91e0e5a fix: invalid types for example options 2022-09-07 23:22:34 +02:00
Sculas
fadf62f594 Merge remote-tracking branch 'origin/main' into main 2022-09-07 23:21:41 +02:00
Sculas
ad3d332e27 fix: handle private companion objects 2022-09-07 23:21:32 +02:00
semantic-release-bot
8f66df7666 chore(release): 4.1.0 [skip ci]
# [4.1.0](https://github.com/revanced/revanced-patcher/compare/v4.0.0...v4.1.0) (2022-09-07)

### Features

* deprecation for patches ([80c2e80](80c2e80925))
2022-09-07 20:32:51 +00:00
Sculas
80c2e80925 feat: deprecation for patches 2022-09-07 22:31:15 +02:00
semantic-release-bot
c3db23d3c7 chore(release): 4.0.0 [skip ci]
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)

### Code Refactoring

* Improve Patch Options ([6b909c1](6b909c1ee6))

### BREAKING CHANGES

* Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
2022-09-07 18:57:04 +00:00
Sculas
c28584736e Merge remote-tracking branch 'origin/main' into main 2022-09-07 20:55:45 +02:00
Sculas
6b909c1ee6 refactor: Improve Patch Options
It's so much better now. Really happy with the current system.

BREAKING CHANGE: Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
2022-09-07 20:55:35 +02:00
Sculas
0e8446516e build: add Kotlin Reflect 2022-09-07 20:52:05 +02:00
16 changed files with 306 additions and 38 deletions

1
.idea/.gitignore generated vendored
View File

@@ -6,3 +6,4 @@
# Datasource local storage ignored files # Datasource local storage ignored files
/dataSources/ /dataSources/
/dataSources.local.xml /dataSources.local.xml
/kotlinc.xml

View File

@@ -1,3 +1,83 @@
## [4.2.2](https://github.com/revanced/revanced-patcher/compare/v4.2.1...v4.2.2) (2022-09-08)
### Bug Fixes
* invalid type propagation in options ([b873228](https://github.com/revanced/revanced-patcher/commit/b873228ef0a9e6e431a4278c979caa5fcc508e0d)), closes [#98](https://github.com/revanced/revanced-patcher/issues/98)
## [4.2.1](https://github.com/revanced/revanced-patcher/compare/v4.2.0...v4.2.1) (2022-09-08)
### Bug Fixes
* make patcher version public ([76c45dd](https://github.com/revanced/revanced-patcher/commit/76c45dd7c1ffdca57e30ae7109c9fe0e5768f877))
# [4.2.0](https://github.com/revanced/revanced-patcher/compare/v4.1.5...v4.2.0) (2022-09-08)
### Bug Fixes
* remove repeatable from PatchDeprecated ([6e73631](https://github.com/revanced/revanced-patcher/commit/6e73631d4d21e5e862f07ed7517244f36394e5ca))
### Features
* SincePatcher annotation ([25f74dc](https://github.com/revanced/revanced-patcher/commit/25f74dc5e9ed1a09258345b920d4f5a0dd7da527))
## [4.1.5](https://github.com/revanced/revanced-patcher/compare/v4.1.4...v4.1.5) (2022-09-08)
### Bug Fixes
* broken deprecation message ([62aa295](https://github.com/revanced/revanced-patcher/commit/62aa295e7372014238415af36d902a4e88e2acbc))
## [4.1.4](https://github.com/revanced/revanced-patcher/compare/v4.1.3...v4.1.4) (2022-09-08)
### Bug Fixes
* handle option types and nulls properly ([aff4968](https://github.com/revanced/revanced-patcher/commit/aff4968e6f67239afa3b5c02cc133a17d9c3cbeb))
## [4.1.3](https://github.com/revanced/revanced-patcher/compare/v4.1.2...v4.1.3) (2022-09-07)
### Bug Fixes
* only run list option check if not null ([4055939](https://github.com/revanced/revanced-patcher/commit/4055939c089e3c396c308c980215d93a1dea5954))
## [4.1.2](https://github.com/revanced/revanced-patcher/compare/v4.1.1...v4.1.2) (2022-09-07)
### Bug Fixes
* invalid types for example options ([79f91e0](https://github.com/revanced/revanced-patcher/commit/79f91e0e5a6d99828f30aae55339ce0d897394c7))
## [4.1.1](https://github.com/revanced/revanced-patcher/compare/v4.1.0...v4.1.1) (2022-09-07)
### Bug Fixes
* handle private companion objects ([ad3d332](https://github.com/revanced/revanced-patcher/commit/ad3d332e27d07e9d074bbaaf51af7eb2f9bfc7d5))
# [4.1.0](https://github.com/revanced/revanced-patcher/compare/v4.0.0...v4.1.0) (2022-09-07)
### Features
* deprecation for patches ([80c2e80](https://github.com/revanced/revanced-patcher/commit/80c2e809251cdb04d2dd3b3bfdbb8844bdfa31fa))
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)
### Code Refactoring
* Improve Patch Options ([6b909c1](https://github.com/revanced/revanced-patcher/commit/6b909c1ee6b8c2ea08bbca059df755e2e5f31656))
### BREAKING CHANGES
* Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06) ## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)

View File

@@ -26,6 +26,7 @@ dependencies {
implementation("app.revanced:multidexlib2:2.5.2.r2") implementation("app.revanced:multidexlib2:2.5.2.r2")
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT") implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
implementation(kotlin("reflect"))
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
} }
@@ -36,6 +37,9 @@ tasks {
events("PASSED", "SKIPPED", "FAILED") events("PASSED", "SKIPPED", "FAILED")
} }
} }
processResources {
expand("projectVersion" to project.version)
}
} }
java { java {

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 3.5.1 version = 4.2.2

View File

@@ -4,7 +4,9 @@ import app.revanced.patcher.data.Data
import app.revanced.patcher.data.PackageMetadata import app.revanced.patcher.data.PackageMetadata
import app.revanced.patcher.data.impl.findIndexed import app.revanced.patcher.data.impl.findIndexed
import app.revanced.patcher.extensions.PatchExtensions.dependencies import app.revanced.patcher.extensions.PatchExtensions.dependencies
import app.revanced.patcher.extensions.PatchExtensions.deprecated
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.extensions.PatchExtensions.sincePatcherVersion
import app.revanced.patcher.extensions.nullOutputStream import app.revanced.patcher.extensions.nullOutputStream
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
@@ -14,6 +16,7 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patcher.util.ListBackedSet import app.revanced.patcher.util.ListBackedSet
import app.revanced.patcher.util.VersionReader
import brut.androlib.Androlib import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework import brut.androlib.meta.UsesFramework
import brut.androlib.options.BuildOptions import brut.androlib.options.BuildOptions
@@ -35,7 +38,7 @@ import java.io.Closeable
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
val NAMER = BasicDexFileNamer() private val NAMER = BasicDexFileNamer()
/** /**
* The ReVanced Patcher. * The ReVanced Patcher.
@@ -47,6 +50,11 @@ class Patcher(private val options: PatcherOptions) {
val data: PatcherData val data: PatcherData
companion object {
@JvmStatic
val version = VersionReader.read()
}
init { init {
val extInputFile = ExtFile(options.inputFile) val extInputFile = ExtFile(options.inputFile)
try { try {
@@ -243,6 +251,15 @@ class Patcher(private val options: PatcherOptions) {
* @param patches [Patch]es The patches to add. * @param patches [Patch]es The patches to add.
*/ */
fun addPatches(patches: Iterable<Class<out Patch<Data>>>) { fun addPatches(patches: Iterable<Class<out Patch<Data>>>) {
for (patch in patches) {
val needsVersion = patch.sincePatcherVersion
if (needsVersion != null && needsVersion > version) {
logger.error("Patch '${patch.patchName}' requires Patcher version $needsVersion or higher")
logger.error("Current Patcher version is $version")
logger.warn("Skipping '${patch.patchName}'!")
continue // TODO: continue or halt/throw?
}
}
data.patches.addAll(patches) data.patches.addAll(patches)
} }
@@ -289,6 +306,11 @@ class Patcher(private val options: PatcherOptions) {
return PatchResultError("'$patchName' is a resource patch, but resource patching is disabled") return PatchResultError("'$patchName' is a resource patch, but resource patching is disabled")
} }
patch.deprecated?.let { (reason, replacement) ->
logger.warn("'$patchName' is deprecated: $reason")
if (replacement != null) logger.warn("Use '${replacement.java.patchName}' instead")
}
// TODO: find a solution for this // TODO: find a solution for this
val data = if (isResourcePatch) { val data = if (isResourcePatch) {
data.resourceData data.resourceData

View File

@@ -0,0 +1,19 @@
package app.revanced.patcher.annotation
import app.revanced.patcher.data.Data
import app.revanced.patcher.patch.Patch
import kotlin.reflect.KClass
/**
* Declares a [Patch] deprecated for removal.
* @param reason The reason why the patch is deprecated.
* @param replacement The replacement for the deprecated patch, if any.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class PatchDeprecated(
val reason: String,
val replacement: KClass<out Patch<Data>> = Patch::class
// Values cannot be nullable in annotations, so this will have to do.
)

View File

@@ -0,0 +1,13 @@
package app.revanced.patcher.annotation
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.Patcher
/**
* Declares a [Patch] deprecated for removal.
* @param version The minimum version of the [Patcher] this [Patch] supports.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class SincePatcher(val version: String)

View File

@@ -1,13 +1,15 @@
package app.revanced.patcher.extensions package app.revanced.patcher.extensions
import app.revanced.patcher.annotation.Compatibility import app.revanced.patcher.annotation.*
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.Data import app.revanced.patcher.data.Data
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchOptions
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.companionObjectInstance
/** /**
* Recursively find a given annotation on a class. * Recursively find a given annotation on a class.
@@ -36,13 +38,27 @@ private fun <T : Annotation> Class<*>.findAnnotationRecursively(
} }
object PatchExtensions { object PatchExtensions {
val Class<out Patch<Data>>.patchName: String val Class<*>.patchName: String
get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version
val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
val Class<out Patch<Data>>.options: PatchOptions?
get() = kotlin.companionObject?.let { cl ->
if (cl.visibility != KVisibility.PUBLIC) return null
kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
}
}
val Class<out Patch<Data>>.deprecated: Pair<String, KClass<out Patch<Data>>?>?
get() = recursiveAnnotation(PatchDeprecated::class)?.let {
it.reason to it.replacement.let { cl ->
if (cl == Patch::class) null else cl
}
}
val Class<out Patch<Data>>.sincePatcherVersion get() = recursiveAnnotation(SincePatcher::class)?.version
@JvmStatic @JvmStatic
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean { fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {

View File

@@ -17,9 +17,18 @@ abstract class Patch<out T : Data> {
* The main function of the [Patch] which the patcher will call. * The main function of the [Patch] which the patcher will call.
*/ */
abstract fun execute(data: @UnsafeVariance T): PatchResult abstract fun execute(data: @UnsafeVariance T): PatchResult
}
abstract class OptionsContainer {
/** /**
* A list of [PatchOption]s. * A list of [PatchOption]s.
* @see PatchOptions
*/ */
open val options = PatchOptions() @Suppress("MemberVisibilityCanBePrivate")
val options = PatchOptions()
protected fun option(opt: PatchOption<*>): PatchOption<*> {
options.register(opt)
return opt
}
} }

View File

@@ -18,13 +18,17 @@ object RequirementNotMetException : Exception("null was passed into an option th
* @param options An array of [PatchOption]s. * @param options An array of [PatchOption]s.
*/ */
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> { class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
private val register = buildMap { private val register = mutableMapOf<String, PatchOption<*>>()
for (option in options) {
if (containsKey(option.key)) { init {
throw IllegalStateException("Multiple options found with the same key") options.forEach { register(it) }
} }
put(option.key, option)
internal fun register(option: PatchOption<*>) {
if (register.containsKey(option.key)) {
throw IllegalStateException("Multiple options found with the same key")
} }
register[option.key] = option
} }
/** /**
@@ -77,6 +81,12 @@ sealed class PatchOption<T>(
val validator: (T?) -> Boolean val validator: (T?) -> Boolean
) { ) {
var value: T? = default var value: T? = default
get() {
if (field == null && required) {
throw RequirementNotMetException
}
return field
}
set(value) { set(value) {
if (value == null && required) { if (value == null && required) {
throw RequirementNotMetException throw RequirementNotMetException
@@ -91,7 +101,13 @@ sealed class PatchOption<T>(
* Gets the value of the option. * Gets the value of the option.
* Please note that using the wrong value type results in a runtime error. * Please note that using the wrong value type results in a runtime error.
*/ */
operator fun <T> getValue(thisRef: Nothing?, property: KProperty<*>) = value as T inline operator fun <reified V> getValue(thisRef: Any?, property: KProperty<*>): V? {
if (value !is V?) throw InvalidTypeException(
V::class.java.canonicalName,
value?.let { it::class.java.canonicalName } ?: "null"
)
return value as? V?
}
/** /**
* Gets the value of the option. * Gets the value of the option.
@@ -154,7 +170,7 @@ sealed class PatchOption<T>(
} }
) { ) {
init { init {
if (default !in options) { if (default != null && default !in options) {
throw IllegalStateException("Default option must be an allowed option") throw IllegalStateException("Default option must be an allowed option")
} }
} }
@@ -223,4 +239,4 @@ sealed class PatchOption<T>(
validator(it?.toFile()) validator(it?.toFile())
} }
) )
} }

View File

@@ -0,0 +1,18 @@
package app.revanced.patcher.util
import java.util.*
internal object VersionReader {
@JvmStatic
private val props = Properties().apply {
load(
VersionReader::class.java.getResourceAsStream("/revanced-patcher/version.properties")
?: throw IllegalStateException("Could not load version.properties")
)
}
@JvmStatic
fun read(): String {
return props.getProperty("version") ?: throw IllegalStateException("Version not found")
}
}

View File

@@ -0,0 +1 @@
version=${projectVersion}

View File

@@ -0,0 +1,18 @@
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)
}
}

View File

@@ -7,7 +7,7 @@ import java.io.File
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
internal class PatchOptionsTest { internal class PatchOptionsTest {
private val options = ExampleBytecodePatch().options private val options = ExampleBytecodePatch.options
@Test @Test
fun `should not throw an exception`() { fun `should not throw an exception`() {
@@ -16,21 +16,25 @@ internal class PatchOptionsTest {
is PatchOption.StringOption -> { is PatchOption.StringOption -> {
option.value = "Hello World" option.value = "Hello World"
} }
is PatchOption.BooleanOption -> { is PatchOption.BooleanOption -> {
option.value = false option.value = false
} }
is PatchOption.StringListOption -> { is PatchOption.StringListOption -> {
option.value = option.options.first() option.value = option.options.first()
for (choice in option.options) { for (choice in option.options) {
println(choice) println(choice)
} }
} }
is PatchOption.IntListOption -> { is PatchOption.IntListOption -> {
option.value = option.options.first() option.value = option.options.first()
for (choice in option.options) { for (choice in option.options) {
println(choice) println(choice)
} }
} }
is PatchOption.PathOption -> { is PatchOption.PathOption -> {
option.value = File("test.txt").toPath() option.value = File("test.txt").toPath()
} }
@@ -44,7 +48,7 @@ internal class PatchOptionsTest {
@Test @Test
fun `should return a different value when changed`() { fun `should return a different value when changed`() {
var value: String by options["key1"] var value: String? by options["key1"]
val current = value + "" // force a copy val current = value + "" // force a copy
value = "Hello, world!" value = "Hello, world!"
assertNotEquals(current, value) assertNotEquals(current, value)
@@ -81,9 +85,16 @@ internal class PatchOptionsTest {
} }
@Test @Test
fun `should fail because of the requirement is not met`() { fun `should fail because the requirement is not met`() {
assertThrows<RequirementNotMetException> { assertThrows<RequirementNotMetException> {
options.nullify("key1") options.nullify("key1")
} }
} }
@Test
fun `should fail because getting a non-initialized option is illegal`() {
assertThrows<RequirementNotMetException> {
println(options["key6"].value)
}
}
} }

View File

@@ -7,8 +7,8 @@ import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.PatchOption import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
@@ -33,6 +33,7 @@ import org.jf.dexlib2.immutable.reference.ImmutableStringReference
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
import org.jf.dexlib2.util.Preconditions import org.jf.dexlib2.util.Preconditions
import java.io.File import java.io.File
import java.nio.file.Path
@Patch @Patch
@Name("example-bytecode-patch") @Name("example-bytecode-patch")
@@ -47,6 +48,10 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
// Get the resolved method by its fingerprint from the resolver cache // Get the resolved method by its fingerprint from the resolver cache
val result = ExampleFingerprint.result!! val result = ExampleFingerprint.result!!
// Patch options
println(key1)
key2 = false
// Get the implementation for the resolved method // Get the implementation for the resolved method
val method = result.mutableMethod val method = result.mutableMethod
val implementation = method.implementation!! val implementation = method.implementation!!
@@ -165,21 +170,36 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
) )
} }
override val options = PatchOptions( companion object : OptionsContainer() {
PatchOption.StringOption( private var key1: String? by option(
"key1", "default", "title", "description", true PatchOption.StringOption(
), "key1", "default", "title", "description", true
PatchOption.BooleanOption( )
"key2", true, "title", "description" // required defaults to false )
), private var key2: Boolean? by option(
PatchOption.StringListOption( PatchOption.BooleanOption(
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description" "key2", true, "title", "description" // required defaults to false
), )
PatchOption.IntListOption( )
"key4", 1, listOf(1, 2, 3), "title", "description" private var key3: String? by option(
), PatchOption.StringListOption(
PatchOption.PathOption( "key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
"key5", File("test.txt").toPath(), "title", "description" )
), )
) private var key4: Int? by option(
PatchOption.IntListOption(
"key4", 1, listOf(1, 2, 3), "title", "description"
)
)
private var key5: Path? by option(
PatchOption.PathOption(
"key5", File("test.txt").toPath(), "title", "description"
)
)
private var key6: String? by option(
PatchOption.StringOption(
"key6", null, "title", "description", true
)
)
}
} }

View File

@@ -0,0 +1,20 @@
package app.revanced.patcher.util
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
internal class VersionReaderTest {
@Test
fun read() {
val version = VersionReader.read()
assertNotNull(version)
assertTrue(version.isNotEmpty())
val parts = version.split(".")
assertEquals(3, parts.size)
parts.forEach {
assertTrue(it.toInt() >= 0)
}
println(version)
}
}