1
mirror of https://github.com/revanced/revanced-patcher synced 2025-09-06 16:38:50 +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)

View File

@@ -26,6 +26,7 @@ dependencies {
implementation("app.revanced:multidexlib2:2.5.2.r2")
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
implementation(kotlin("reflect"))
testImplementation(kotlin("test"))
}

View File

@@ -1,2 +1,2 @@
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
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")
}

View File

@@ -24,5 +24,5 @@ annotation class Compatibility(
@MustBeDocumented
annotation class Package(
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.data.Data
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.Patch
import kotlin.reflect.KClass
import kotlin.reflect.full.companionObjectInstance
/**
* 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>>.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>>.options get() = kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
}
@JvmStatic
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.
*/
abstract class MethodFingerprint(
internal val returnType: String?,
internal val access: Int?,
internal val parameters: Iterable<String>?,
internal val opcodes: Iterable<Opcode?>?,
internal val returnType: String? = null,
internal val access: Int? = null,
internal val parameters: Iterable<String>? = null,
internal val opcodes: Iterable<Opcode?>? = null,
internal val strings: Iterable<String>? = null,
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
) : Fingerprint {

View File

@@ -17,9 +17,18 @@ abstract class Patch<out T : Data> {
* The main function of the [Patch] which the patcher will call.
*/
abstract fun execute(data: @UnsafeVariance T): PatchResult
}
abstract class OptionsContainer {
/**
* 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
import java.io.File
import java.nio.file.Path
import kotlin.reflect.KProperty
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) :
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.
* @param options An array of [PatchOption]s.
*/
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
private val register = buildMap {
for (option in options) {
if (containsKey(option.key)) {
throw IllegalStateException("Multiple options found with the same key")
}
put(option.key, option)
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
}
/**
@@ -77,7 +83,7 @@ sealed class PatchOption<T>(
var value: T? = default
set(value) {
if (value == null && required) {
throw RequirementNotMetException()
throw RequirementNotMetException
}
if (!validator(value)) {
throw IllegalValueException(value)
@@ -89,7 +95,7 @@ sealed class PatchOption<T>(
* Gets the value of the option.
* 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.
@@ -153,7 +159,7 @@ sealed class PatchOption<T>(
) {
init {
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>(
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 org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.io.File
import kotlin.test.assertNotEquals
internal class PatchOptionsTest {
private val options = ExampleBytecodePatch().options
private val options = ExampleBytecodePatch.options
@Test
fun `should not throw an exception`() {
@@ -15,21 +16,28 @@ internal class PatchOptionsTest {
is PatchOption.StringOption -> {
option.value = "Hello World"
}
is PatchOption.BooleanOption -> {
option.value = false
}
is PatchOption.StringListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
}
}
is PatchOption.IntListOption -> {
option.value = option.options.first()
for (choice in option.options) {
println(choice)
}
}
is PatchOption.PathOption -> {
option.value = File("test.txt").toPath()
}
}
}
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.or
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
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.value.ImmutableFieldEncodedValue
import org.jf.dexlib2.util.Preconditions
import java.io.File
import java.nio.file.Path
@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
val result = ExampleFingerprint.result!!
// Patch options
println(key1)
key2 = false
// Get the implementation for the resolved method
val method = result.mutableMethod
val implementation = method.implementation!!
@@ -164,18 +170,31 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
)
}
override val options = PatchOptions(
PatchOption.StringOption(
"key1", "default", "title", "description", true
),
PatchOption.BooleanOption(
"key2", true, "title", "description" // required defaults to false
),
PatchOption.StringListOption(
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
),
PatchOption.IntListOption(
"key4", 1, listOf(1, 2, 3), "title", "description"
),
)
companion object : OptionsContainer() {
private var key1: String by option(
PatchOption.StringOption(
"key1", "default", "title", "description", true
)
)
private var key2: Boolean by option(
PatchOption.BooleanOption(
"key2", true, "title", "description" // required defaults to false
)
)
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"
)
)
}
}