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

Compare commits

...

19 Commits

Author SHA1 Message Date
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
semantic-release-bot
aa46b953db chore(release): 3.5.1 [skip ci]
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)

### Bug Fixes

* add tests for PathOption ([d6308e1](d6308e126c))
* PathOption should be open, not sealed ([a562e47](a562e476c0))
* typo in ListOption ([3921648](392164862c))

### Performance Improvements

* make exception an object ([75d2be8](75d2be8803))
2022-09-06 20:38:24 +00:00
Sculas
a562e476c0 fix: PathOption should be open, not sealed 2022-09-06 22:36:20 +02:00
Sculas
75d2be8803 perf: make exception an object 2022-09-06 22:35:33 +02:00
Sculas
d6308e126c fix: add tests for PathOption 2022-09-06 22:35:00 +02:00
Sculas
bb97af4d86 refactor: add FileOption alias for PathOption 2022-09-06 22:34:46 +02:00
Sculas
392164862c fix: typo in ListOption 2022-09-06 21:44:05 +02:00
Sculas
53e807dec1 refactor: add PatchOption.PathOption 2022-09-06 21:43:45 +02:00
semantic-release-bot
288d50a8b4 chore(release): 3.5.0 [skip ci]
# [3.5.0](https://github.com/revanced/revanced-patcher/compare/v3.4.1...v3.5.0) (2022-09-05)

### Features

* default value for `Package.versions` annotation parameter ([131dedd](131dedd4b0))
2022-09-05 14:45:56 +00:00
oSumAtrIX
131dedd4b0 feat: default value for Package.versions annotation parameter
Reverts 4b81318710
2022-09-05 16:44:35 +02:00
semantic-release-bot
5a92d5c29d chore(release): 3.4.1 [skip ci]
## [3.4.1](https://github.com/revanced/revanced-patcher/compare/v3.4.0...v3.4.1) (2022-09-03)

### Bug Fixes

* remove default param from Package.versions ([4b81318](4b81318710))
2022-09-03 20:54:06 +00:00
Sculas
4b81318710 fix: remove default param from Package.versions
Kotlin compiler bug produces invalid bytecode, resulting in an IncompleteAnnotationException at runtime.
2022-09-03 22:52:49 +02:00
semantic-release-bot
44f6a3ebc5 chore(release): 3.4.0 [skip ci]
# [3.4.0](https://github.com/revanced/revanced-patcher/compare/v3.3.3...v3.4.0) (2022-08-31)

### Features

* nullable parameters ([7882a8d](7882a8d928))
2022-08-31 18:32:43 +00:00
oSumAtrIX
7882a8d928 feat: nullable parameters
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-08-31 20:30:31 +02:00
semantic-release-bot
cc3d32748b chore(release): 3.3.3 [skip ci]
## [3.3.3](https://github.com/revanced/revanced-patcher/compare/v3.3.2...v3.3.3) (2022-08-14)

### Bug Fixes

* show error message if cause is null ([f9da2ad](f9da2ad531))
2022-08-14 15:25:16 +00:00
oSumAtrIX
f9da2ad531 fix: show error message if cause is null 2022-08-14 17:22:43 +02:00
11 changed files with 170 additions and 35 deletions

View File

@@ -1,3 +1,57 @@
# [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)
### Bug Fixes
* add tests for PathOption ([d6308e1](https://github.com/revanced/revanced-patcher/commit/d6308e126c6217b098192c51b6e98bc85a8656bd))
* PathOption should be open, not sealed ([a562e47](https://github.com/revanced/revanced-patcher/commit/a562e476c085841efbc7ee98b01d8e6bb18ed757))
* typo in ListOption ([3921648](https://github.com/revanced/revanced-patcher/commit/392164862c83d6e76b2a2113d6f6d59fef0020d1))
### Performance Improvements
* make exception an object ([75d2be8](https://github.com/revanced/revanced-patcher/commit/75d2be88037c9cf5436ab69d92abea575409a865))
# [3.5.0](https://github.com/revanced/revanced-patcher/compare/v3.4.1...v3.5.0) (2022-09-05)
### Features
* default value for `Package.versions` annotation parameter ([131dedd](https://github.com/revanced/revanced-patcher/commit/131dedd4b021fe1c3b0be49ccba4764b325770ea))
## [3.4.1](https://github.com/revanced/revanced-patcher/compare/v3.4.0...v3.4.1) (2022-09-03)
### Bug Fixes
* remove default param from Package.versions ([4b81318](https://github.com/revanced/revanced-patcher/commit/4b813187107e85dc267dbc2d353884b2cc671cc4))
# [3.4.0](https://github.com/revanced/revanced-patcher/compare/v3.3.3...v3.4.0) (2022-08-31)
### Features
* nullable parameters ([7882a8d](https://github.com/revanced/revanced-patcher/commit/7882a8d928cad8de8cfea711947fc02659549d20))
## [3.3.3](https://github.com/revanced/revanced-patcher/compare/v3.3.2...v3.3.3) (2022-08-14)
### Bug Fixes
* show error message if cause is null ([f9da2ad](https://github.com/revanced/revanced-patcher/commit/f9da2ad531644617ad5a2cc6a1819d530e18ba22))
## [3.3.2](https://github.com/revanced/revanced-patcher/compare/v3.3.1...v3.3.2) (2022-08-06) ## [3.3.2](https://github.com/revanced/revanced-patcher/compare/v3.3.1...v3.3.2) (2022-08-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"))
} }

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 3.3.2 version = 4.0.0

View File

@@ -276,7 +276,8 @@ class Patcher(private val options: PatcherOptions) {
if (result.isSuccess()) return@forEach if (result.isSuccess()) return@forEach
val errorMessage = result.error()!!.cause val error = result.error()!!
val errorMessage = error.cause ?: error.message
return PatchResultError("'$patchName' depends on '${patchDependency.patchName}' but the following error was raised: $errorMessage") return PatchResultError("'$patchName' depends on '${patchDependency.patchName}' but the following error was raised: $errorMessage")
} }

View File

@@ -24,5 +24,5 @@ annotation class Compatibility(
@MustBeDocumented @MustBeDocumented
annotation class Package( annotation class Package(
val name: String, val name: String,
val versions: Array<String> val versions: Array<String> = [],
) )

View File

@@ -6,8 +6,10 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version 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 kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.companionObjectInstance
/** /**
* Recursively find a given annotation on a class. * Recursively find a given annotation on a class.
@@ -43,6 +45,9 @@ object PatchExtensions {
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 get() = kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
}
@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

@@ -22,10 +22,10 @@ import org.jf.dexlib2.iface.Method
* A `null` opcode is equals to an unknown opcode. * A `null` opcode is equals to an unknown opcode.
*/ */
abstract class MethodFingerprint( abstract class MethodFingerprint(
internal val returnType: String?, internal val returnType: String? = null,
internal val access: Int?, internal val access: Int? = null,
internal val parameters: Iterable<String>?, internal val parameters: Iterable<String>? = null,
internal val opcodes: Iterable<Opcode?>?, internal val opcodes: Iterable<Opcode?>? = null,
internal val strings: Iterable<String>? = null, internal val strings: Iterable<String>? = null,
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
) : Fingerprint { ) : Fingerprint {

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

@@ -2,6 +2,8 @@
package app.revanced.patcher.patch package app.revanced.patcher.patch
import java.io.File
import java.nio.file.Path
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class NoSuchOptionException(val option: String) : Exception("No such option: $option") class NoSuchOptionException(val option: String) : Exception("No such option: $option")
@@ -9,20 +11,24 @@ class IllegalValueException(val value: Any?) : Exception("Illegal value: $value"
class InvalidTypeException(val got: String, val expected: String) : class InvalidTypeException(val got: String, val expected: String) :
Exception("Invalid option value type: $got, expected $expected") Exception("Invalid option value type: $got, expected $expected")
class RequirementNotMetException : Exception("null was passed into an option that requires a value") object RequirementNotMetException : Exception("null was passed into an option that requires a value")
/** /**
* A registry for an array of [PatchOption]s. * A registry for an array of [PatchOption]s.
* @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,7 +83,7 @@ sealed class PatchOption<T>(
var value: T? = default var value: T? = default
set(value) { set(value) {
if (value == null && required) { if (value == null && required) {
throw RequirementNotMetException() throw RequirementNotMetException
} }
if (!validator(value)) { if (!validator(value)) {
throw IllegalValueException(value) throw IllegalValueException(value)
@@ -89,7 +95,7 @@ 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 operator fun <T> getValue(thisRef: Any?, property: KProperty<*>) = value as T
/** /**
* Gets the value of the option. * Gets the value of the option.
@@ -153,7 +159,7 @@ sealed class PatchOption<T>(
) { ) {
init { init {
if (default !in options) { if (default !in options) {
throw IllegalStateException("Default option must be an allowed options") throw IllegalStateException("Default option must be an allowed option")
} }
} }
} }
@@ -189,4 +195,36 @@ sealed class PatchOption<T>(
) : ListOption<Int>( ) : ListOption<Int>(
key, default, options, title, description, required, validator key, default, options, title, description, required, validator
) )
/**
* A [PatchOption] representing a [Path].
* @see PatchOption
*/
open class PathOption(
key: String,
default: Path?,
title: String,
description: String,
required: Boolean = false,
validator: (Path?) -> Boolean = { true }
) : PatchOption<Path>(
key, default, title, description, required, validator
)
/**
* A [PathOption] of type [File].
* @see PathOption
*/
class FileOption(
key: String,
default: File?,
title: String,
description: String,
required: Boolean = false,
validator: (File?) -> Boolean = { true }
) : PathOption(
key, default?.toPath(), title, description, required, {
validator(it?.toFile())
}
)
} }

View File

@@ -3,10 +3,11 @@ package app.revanced.patcher.patch
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
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`() {
@@ -15,21 +16,28 @@ 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 -> {
option.value = File("test.txt").toPath()
}
} }
} }
val option = options["key1"] val option = options["key1"]

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
@@ -32,6 +32,8 @@ import org.jf.dexlib2.immutable.reference.ImmutableFieldReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference 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.nio.file.Path
@Patch @Patch
@Name("example-bytecode-patch") @Name("example-bytecode-patch")
@@ -46,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!!
@@ -164,18 +170,31 @@ 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: List<String> by option(
), PatchOption.StringListOption(
) "key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
)
)
private var key4: List<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"
)
)
}
} }