From 6c65952d80a795a3ef4a37877123e9375025d3ae Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 1 Jun 2022 01:33:30 +0200 Subject: [PATCH] feat: patch dependencies annotation and `PatcherOptions` --- .../kotlin/app/revanced/patcher/Patcher.kt | 133 +++++++++--------- .../app/revanced/patcher/PatcherOptions.kt | 15 ++ .../app/revanced/patcher/data/PatcherData.kt | 4 +- .../data/implementation/BytecodeData.kt | 47 ++----- .../extensions/AnnotationExtensions.kt | 53 +++++-- .../revanced/patcher/extensions/Extensions.kt | 48 ++----- .../patch/annotations/PatchAnnotation.kt | 7 +- .../app/revanced/patcher/patch/base/Patch.kt | 3 +- .../patch/implementation/misc/PatchResult.kt | 10 +- .../implementation/method/MethodSignature.kt | 20 +-- .../resolver/MethodSignatureResolver.kt | 7 +- .../patcher/util/ProxyBackedClassList.kt | 15 +- .../patcher/util/method/MethodWalker.kt | 2 +- .../patcher/util/patch/PatchLoader.kt | 34 ----- .../patcher/util/patch/base/PatchBundle.kt | 3 +- 15 files changed, 180 insertions(+), 221 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patcher/PatcherOptions.kt delete mode 100644 src/main/kotlin/app/revanced/patcher/util/patch/PatchLoader.kt diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 784cde5c..2fb0a116 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -1,16 +1,17 @@ package app.revanced.patcher -import app.revanced.patcher.annotation.Name import app.revanced.patcher.data.PatcherData import app.revanced.patcher.data.base.Data import app.revanced.patcher.data.implementation.findIndexed -import app.revanced.patcher.extensions.findAnnotationRecursively +import app.revanced.patcher.extensions.PatchExtensions.dependencies +import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.nullOutputStream import app.revanced.patcher.patch.base.Patch import app.revanced.patcher.patch.implementation.BytecodePatch import app.revanced.patcher.patch.implementation.ResourcePatch +import app.revanced.patcher.patch.implementation.misc.PatchResult +import app.revanced.patcher.patch.implementation.misc.PatchResultError import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess -import app.revanced.patcher.signature.implementation.method.MethodSignature import app.revanced.patcher.signature.implementation.method.resolver.MethodSignatureResolver import app.revanced.patcher.util.ListBackedSet import brut.androlib.Androlib @@ -34,15 +35,10 @@ val NAMER = BasicDexFileNamer() /** * The ReVanced Patcher. - * @param inputFile The input file (usually an apk file). - * @param resourceCacheDirectory Directory to cache resources. - * @param patchResources Weather to use the resource patcher. Resources will still need to be decoded. + * @param options The options for the patcher. */ class Patcher( - inputFile: File, - // TODO: maybe a file system in memory is better. Could cause high memory usage. - private val resourceCacheDirectory: String, - private val patchResources: Boolean = false + private val options: PatcherOptions ) { val packageVersion: String val packageName: String @@ -50,11 +46,10 @@ class Patcher( private lateinit var usesFramework: UsesFramework private val patcherData: PatcherData private val opcodes: Opcodes - private var signaturesResolved = false init { - val extFileInput = ExtFile(inputFile) - val outDir = File(resourceCacheDirectory) + val extFileInput = ExtFile(options.inputFile) + val outDir = File(options.resourceCacheDirectory) if (outDir.exists()) outDir.deleteRecursively() outDir.mkdir() @@ -63,7 +58,7 @@ class Patcher( val androlib = Androlib() val resourceTable = androlib.getResTable(extFileInput, true) - if (patchResources) { + if (options.patchResources) { // 1. decode resources to cache directory androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable) androlib.decodeResourcesFull(extFileInput, outDir, resourceTable) @@ -93,11 +88,11 @@ class Patcher( packageVersion = resourceTable.versionInfo.versionName packageName = resourceTable.currentResPackage.name // read dex files - val dexFile = MultiDexIO.readDexFile(true, inputFile, NAMER, null, null) + val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null) opcodes = dexFile.opcodes // save to patcher data - patcherData = PatcherData(dexFile.classes.toMutableList(), resourceCacheDirectory) + patcherData = PatcherData(dexFile.classes.toMutableList(), options.resourceCacheDirectory) } /** @@ -144,8 +139,8 @@ class Patcher( } // build modified resources - if (patchResources) { - val extDir = ExtFile(resourceCacheDirectory) + if (options.patchResources) { + val extDir = ExtFile(options.resourceCacheDirectory) // TODO: figure out why a new instance of Androlib is necessary here Androlib().buildResources(extDir, usesFramework) @@ -161,33 +156,62 @@ class Patcher( } /** - * Add a patch to the patcher. - * @param patches The patches to add. + * Add [Patch]es to the patcher. + * @param patches [Patch]es The patches to add. */ - fun addPatches(patches: Iterable>) { + fun addPatches(patches: Iterable>>) { patcherData.patches.addAll(patches) } /** - * Resolves all signatures. + * Apply a [patch] and its dependencies recursively. + * @param patch The [patch] to apply. + * @param appliedPatches A list of [patch] names, to prevent applying [patch]es twice. + * @return The result of executing the [patch]. */ - fun resolveSignatures(): List { - val signatures = buildList { - for (patch in patcherData.patches) { - if (patch !is BytecodePatch) continue - this.addAll(patch.signatures) - } + private fun applyPatch( + patch: Class>, appliedPatches: MutableList + ): PatchResult { + val patchName = patch.patchName + + // if the patch has already applied silently skip it + if (appliedPatches.contains(patchName)) return PatchResultSuccess() + appliedPatches.add(patchName) + + // recursively apply all dependency patches + patch.dependencies?.forEach { + val patchDependency = it.java + + val result = applyPatch(patchDependency, appliedPatches) + if (result.isSuccess()) return@forEach + + val errorMessage = result.error()!!.message + return PatchResultError("$patchName depends on ${patchDependency.patchName} but the following error was raised: $errorMessage") } - if (signatures.isEmpty()) { - return emptyList() + + val patchInstance = patch.getDeclaredConstructor().newInstance() + + // if the current patch is a resource patch but resource patching is disabled, return an error + val isResourcePatch = patchInstance is ResourcePatch + if (!options.patchResources && isResourcePatch) return PatchResultError("$patchName is a resource patch, but resource patching is disabled.") + + // TODO: find a solution for this + val data = if (isResourcePatch) { + patcherData.resourceData + } else { + MethodSignatureResolver( + patcherData.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures + ).resolve(patcherData) + patcherData.bytecodeData } - MethodSignatureResolver(patcherData.bytecodeData.classes.internalClasses, signatures).resolve(patcherData) - signaturesResolved = true - return signatures + return try { + patchInstance.execute(data) + } catch (e: Exception) { + PatchResultError(e) + } } - /** * Apply patches loaded into the patcher. * @param stopOnError If true, the patches will stop on the first error. @@ -198,43 +222,22 @@ class Patcher( fun applyPatches( stopOnError: Boolean = false, callback: (String) -> Unit = {} ): Map> { - if (!signaturesResolved) { - resolveSignatures() - } + val appliedPatches = mutableListOf() + return buildMap { for (patch in patcherData.patches) { - val resourcePatch = patch is ResourcePatch - if (!patchResources && resourcePatch) continue - - val patchNameAnnotation = patch::class.java.findAnnotationRecursively(Name::class.java) - - patchNameAnnotation?.let { - callback(it.name) - } + val result = applyPatch(patch, appliedPatches) - val result: Result = try { - val data = if (resourcePatch) { - patcherData.resourceData - } else { - patcherData.bytecodeData - } - - val pr = patch.execute(data) - - if (pr.isSuccess()) { - Result.success(pr.success()!!) - } else { - Result.failure(Exception(pr.error()?.errorMessage() ?: "Unknown error")) - } - } catch (e: Exception) { - Result.failure(e) - } + val name = patch.patchName + callback(name) - patchNameAnnotation?.let { - this[patchNameAnnotation.name] = result + this[name] = if (result.isSuccess()) { + Result.success(result.success()!!) + } else { + Result.failure(result.error()!!) } - if (result.isFailure && stopOnError) break + if (stopOnError && result.isError()) break } } } diff --git a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt new file mode 100644 index 00000000..038826d8 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt @@ -0,0 +1,15 @@ +package app.revanced.patcher + +import java.io.File + +/** + * @param inputFile The input file (usually an apk file). + * @param resourceCacheDirectory Directory to cache resources. + * @param patchResources Weather to use the resource patcher. Resources will still need to be decoded. + */ +data class PatcherOptions( + internal val inputFile: File, + // TODO: maybe a file system in memory is better. Could cause high memory usage. + internal val resourceCacheDirectory: String, + internal val patchResources: Boolean = false +) diff --git a/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt b/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt index f225d8f6..2462d420 100644 --- a/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt +++ b/src/main/kotlin/app/revanced/patcher/data/PatcherData.kt @@ -11,8 +11,8 @@ internal data class PatcherData( val internalClasses: MutableList, val resourceCacheDirectory: String ) { - internal val patches = mutableListOf>() + internal val patches = mutableListOf>>() - internal val bytecodeData = BytecodeData(patches, internalClasses) + internal val bytecodeData = BytecodeData(internalClasses) internal val resourceData = ResourceData(File(resourceCacheDirectory)) } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/data/implementation/BytecodeData.kt b/src/main/kotlin/app/revanced/patcher/data/implementation/BytecodeData.kt index 03d1ae5d..6e8e7e18 100644 --- a/src/main/kotlin/app/revanced/patcher/data/implementation/BytecodeData.kt +++ b/src/main/kotlin/app/revanced/patcher/data/implementation/BytecodeData.kt @@ -1,55 +1,33 @@ package app.revanced.patcher.data.implementation import app.revanced.patcher.data.base.Data -import app.revanced.patcher.patch.base.Patch -import app.revanced.patcher.patch.implementation.BytecodePatch -import app.revanced.patcher.signature.implementation.method.resolver.SignatureResolverResult import app.revanced.patcher.util.ProxyBackedClassList import app.revanced.patcher.util.method.MethodWalker import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.Method class BytecodeData( - // FIXME: ugly solution due to design. - // It does not make sense for a BytecodeData instance to have access to the patches - private val patches: List>, internalClasses: MutableList ) : Data { val classes = ProxyBackedClassList(internalClasses) /** - * Find a class by a given class name - * @return A proxy for the first class that matches the class name + * Find a class by a given class name. + * @param className The name of the class. + * @return A proxy for the first class that matches the class name. */ fun findClass(className: String) = findClass { it.type.contains(className) } /** - * Find a class by a given predicate - * @return A proxy for the first class that matches the predicate + * Find a class by a given predicate. + * @param predicate A predicate to match the class. + * @return A proxy for the first class that matches the predicate. */ - fun findClass(predicate: (ClassDef) -> Boolean): app.revanced.patcher.util.proxy.ClassProxy? { + fun findClass(predicate: (ClassDef) -> Boolean) = // if we already proxied the class matching the predicate... - for (patch in patches) { - if (patch !is BytecodePatch) continue - for (signature in patch.signatures) { - val result = signature.result - result ?: continue - - if (predicate(result.definingClassProxy.immutableClass)) return result.definingClassProxy // ...then return that proxy - } - } + classes.proxies.firstOrNull { predicate(it.immutableClass) } ?: // else resolve the class to a proxy and return it, if the predicate is matching a class - return classes.find(predicate)?.let { - proxy(it) - } - } -} - - -class MethodMap : LinkedHashMap() { - override fun get(key: String): SignatureResolverResult { - return super.get(key) ?: throw MethodNotFoundException("Method $key was not found in the method cache") - } + classes.find(predicate)?.let { proxy(it) } } internal class MethodNotFoundException(s: String) : Exception(s) @@ -63,6 +41,11 @@ internal inline fun Iterable.find(predicate: (T) -> Boolean): T? return null } +/** + * Create a [MethodWalker] instance for the current [BytecodeData]. + * @param startMethod The method to start at. + * @return A [MethodWalker] instance. + */ fun BytecodeData.toMethodWalker(startMethod: Method): MethodWalker { return MethodWalker(this, startMethod) } @@ -80,7 +63,7 @@ fun BytecodeData.proxy(classDef: ClassDef): app.revanced.patcher.util.proxy.Clas var proxy = this.classes.proxies.find { it.immutableClass.type == classDef.type } if (proxy == null) { proxy = app.revanced.patcher.util.proxy.ClassProxy(classDef) - this.classes.proxies.add(proxy) + this.classes.add(proxy) } return proxy } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt index 13f3104b..9ef5df1c 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt @@ -4,29 +4,54 @@ import app.revanced.patcher.annotation.Compatibility import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.base.Data import app.revanced.patcher.patch.base.Patch import app.revanced.patcher.signature.implementation.method.MethodSignature import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod +import kotlin.reflect.KClass -private inline fun Any.firstAnnotation() = - this::class.annotations.first { it is T } as T +/** + * Recursively find a given annotation on a class. + * @param targetAnnotation The annotation to find. + * @return The annotation. + */ +private fun Class<*>.recursiveAnnotation(targetAnnotation: KClass) = + this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf()) -private inline fun Any.recursiveAnnotation() = - this::class.java.findAnnotationRecursively(T::class.java)!! + +private fun Class<*>.findAnnotationRecursively( + targetAnnotation: Class, traversed: MutableSet +): T? { + val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name } + + @Suppress("UNCHECKED_CAST") if (found != null) return found as T + + for (annotation in this.annotations) { + if (traversed.contains(annotation)) continue + traversed.add(annotation) + + return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue + } + + return null +} object PatchExtensions { - val Patch<*>.name get() = firstAnnotation().name - val Patch<*>.version get() = firstAnnotation().version - val Patch<*>.description get() = firstAnnotation().description - val Patch<*>.compatiblePackages get() = recursiveAnnotation().compatiblePackages + val Class>.patchName: String + get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName + val Class>.version get() = recursiveAnnotation(Version::class)?.version + val Class>.description get() = recursiveAnnotation(Description::class)?.description + val Class>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)?.dependencies + val Class>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages } object MethodSignatureExtensions { - val MethodSignature.name get() = firstAnnotation().name - val MethodSignature.version get() = firstAnnotation().version - val MethodSignature.description get() = firstAnnotation().description - val MethodSignature.compatiblePackages get() = recursiveAnnotation().compatiblePackages - val MethodSignature.matchingMethod get() = firstAnnotation() - val MethodSignature.fuzzyThreshold get() = firstAnnotation().threshold + val MethodSignature.name get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName + val MethodSignature.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1" + val MethodSignature.description get() = javaClass.recursiveAnnotation(Description::class)?.description + val MethodSignature.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages + val MethodSignature.matchingMethod get() = javaClass.recursiveAnnotation(MatchingMethod::class) + val MethodSignature.fuzzyPatternScanMethod get() = javaClass.recursiveAnnotation(FuzzyPatternScanMethod::class) + val MethodSignature.fuzzyThreshold get() = fuzzyPatternScanMethod?.threshold ?: 0 } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt index 68286137..5f081c35 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt @@ -11,33 +11,6 @@ import org.jf.dexlib2.immutable.ImmutableMethodImplementation import org.jf.dexlib2.util.MethodUtil import java.io.OutputStream -/** - * Recursively find a given annotation on a class - * @param targetAnnotation The annotation to find - * @return The annotation - */ -fun Class<*>.findAnnotationRecursively(targetAnnotation: Class) = - this.findAnnotationRecursively(targetAnnotation, mutableSetOf()) - -private fun Class<*>.findAnnotationRecursively( - targetAnnotation: Class, - traversed: MutableSet -): T? { - val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name } - - @Suppress("UNCHECKED_CAST") - if (found != null) return found as T - - for (annotation in this.annotations) { - if (traversed.contains(annotation)) continue - traversed.add(annotation) - - return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue - } - - return null -} - infix fun AccessFlags.or(other: AccessFlags) = this.value or other.value infix fun Int.or(other: AccessFlags) = this or other.value @@ -47,6 +20,19 @@ fun MutableMethodImplementation.addInstructions(index: Int, instructions: List, diff --git a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt index 4803b02e..df2f0490 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/annotations/PatchAnnotation.kt @@ -1,9 +1,14 @@ package app.revanced.patcher.patch.annotations +import app.revanced.patcher.data.base.Data +import kotlin.reflect.KClass + /** * Annotation to mark a Class as a patch. */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @MustBeDocumented -annotation class Patch \ No newline at end of file +annotation class Patch( + val dependencies: Array>> = [] +) diff --git a/src/main/kotlin/app/revanced/patcher/patch/base/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/base/Patch.kt index a207c716..80a26352 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/base/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/base/Patch.kt @@ -11,8 +11,9 @@ import app.revanced.patcher.patch.implementation.misc.PatchResult * Can either be a [ResourcePatch] or a [BytecodePatch]. */ abstract class Patch { + /** * The main function of the [Patch] which the patcher will call. */ - abstract fun execute(data: @UnsafeVariance T): PatchResult // FIXME: remove the UnsafeVariance annotation + abstract fun execute(data: @UnsafeVariance T): PatchResult } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/patch/implementation/misc/PatchResult.kt b/src/main/kotlin/app/revanced/patcher/patch/implementation/misc/PatchResult.kt index 7c0b0687..9d6e37ab 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/implementation/misc/PatchResult.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/implementation/misc/PatchResult.kt @@ -24,10 +24,12 @@ interface PatchResult { } } -class PatchResultError(private val errorMessage: String) : PatchResult { - fun errorMessage(): String { - return errorMessage - } +class PatchResultError( + errorMessage: String?, cause: Exception? +) : Exception(errorMessage, cause), PatchResult { + constructor(errorMessage: String) : this(errorMessage, null) + constructor(cause: Exception) : this(cause.message, cause) + } class PatchResultSuccess : PatchResult \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/MethodSignature.kt b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/MethodSignature.kt index 8d0a8cc2..49e47f50 100644 --- a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/MethodSignature.kt +++ b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/MethodSignature.kt @@ -1,7 +1,7 @@ package app.revanced.patcher.signature.implementation.method -import app.revanced.patcher.annotation.Name import app.revanced.patcher.data.implementation.MethodNotFoundException +import app.revanced.patcher.extensions.MethodSignatureExtensions.name import app.revanced.patcher.signature.base.Signature import app.revanced.patcher.signature.implementation.method.resolver.SignatureResolverResult import org.jf.dexlib2.Opcode @@ -26,22 +26,8 @@ abstract class MethodSignature( * The result of the signature */ var result: SignatureResolverResult? = null + @Throws(MethodNotFoundException::class) get() { - return field ?: throw MethodNotFoundException( - "Could not resolve required signature ${ - (this::class.annotations.find { it is Name }?.let { - (it as Name).name - }) - }" - ) - } - val resolved: Boolean - get() { - var resolved = false - try { - resolved = result != null - } catch (_: Exception) { - } - return resolved + return field ?: throw MethodNotFoundException("Could not resolve required signature ${this.name}") } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt index de3634bb..75255a47 100644 --- a/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt +++ b/src/main/kotlin/app/revanced/patcher/signature/implementation/method/resolver/MethodSignatureResolver.kt @@ -2,10 +2,9 @@ package app.revanced.patcher.signature.implementation.method.resolver import app.revanced.patcher.data.PatcherData import app.revanced.patcher.data.implementation.proxy -import app.revanced.patcher.extensions.findAnnotationRecursively +import app.revanced.patcher.extensions.MethodSignatureExtensions.fuzzyThreshold import app.revanced.patcher.extensions.parametersEqual import app.revanced.patcher.signature.implementation.method.MethodSignature -import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod import org.jf.dexlib2.Opcode import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.Method @@ -109,9 +108,7 @@ internal class MethodSignatureResolver( val pattern = signature.opcodes!! val size = pattern.count() - val threshold = - signature::class.java.findAnnotationRecursively(FuzzyPatternScanMethod::class.java)?.threshold - ?: 0 + val threshold = signature.fuzzyThreshold for (instructionIndex in 0 until count) { var patternIndex = 0 diff --git a/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt b/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt index 93e6f82c..925a7742 100644 --- a/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt +++ b/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt @@ -1,24 +1,21 @@ package app.revanced.patcher.util +import app.revanced.patcher.util.proxy.ClassProxy import org.jf.dexlib2.iface.ClassDef class ProxyBackedClassList(internal val internalClasses: MutableList) : List { - internal val proxies = mutableListOf() + private val internalProxies = mutableListOf() + internal val proxies: List = internalProxies - fun add(classDef: ClassDef) { - internalClasses.add(classDef) - } - - fun add(classProxy: app.revanced.patcher.util.proxy.ClassProxy) { - proxies.add(classProxy) - } + fun add(classDef: ClassDef) = internalClasses.add(classDef) + fun add(classProxy: ClassProxy) = internalProxies.add(classProxy) /** * Apply all resolved classes into [internalClasses] and clean the [proxies] list. */ internal fun applyProxies() { // FIXME: check if this could cause issues when multiple patches use the same proxy - proxies.removeIf { proxy -> + internalProxies.removeIf { proxy -> // if the proxy is unused, keep it in the list if (!proxy.proxyUsed) return@removeIf false diff --git a/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt b/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt index 64d69b15..0b1c10e9 100644 --- a/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt +++ b/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt @@ -33,7 +33,7 @@ class MethodWalker internal constructor( * @param walkMutable If this is true, the class of the method will be resolved mutably. * The current method will be mutable. */ - fun walk(offset: Int, walkMutable: Boolean = false): MethodWalker { + fun nextMethod(offset: Int, walkMutable: Boolean = false): MethodWalker { currentMethod.implementation?.instructions?.let { instructions -> val instruction = instructions.elementAt(offset) diff --git a/src/main/kotlin/app/revanced/patcher/util/patch/PatchLoader.kt b/src/main/kotlin/app/revanced/patcher/util/patch/PatchLoader.kt deleted file mode 100644 index b11e4f03..00000000 --- a/src/main/kotlin/app/revanced/patcher/util/patch/PatchLoader.kt +++ /dev/null @@ -1,34 +0,0 @@ -package app.revanced.patcher.util.patch - -import app.revanced.patcher.patch.base.Patch -import java.io.File -import java.net.URLClassLoader -import java.util.jar.JarFile - -object PatchLoader { - /** - * This method loads patches from a given jar file containing [Patch]es - * @return the loaded patches represented as a list of [Patch] classes - */ - fun loadFromFile(patchesJar: File) = buildList { - val jarFile = JarFile(patchesJar) - val classLoader = URLClassLoader(arrayOf(patchesJar.toURI().toURL())) - - val entries = jarFile.entries() - while (entries.hasMoreElements()) { - val entry = entries.nextElement() - if (!entry.name.endsWith(".class") || entry.name.contains("$")) continue - - val clazz = classLoader.loadClass(entry.realName.replace('/', '.').replace(".class", "")) - - if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) continue - - @Suppress("UNCHECKED_CAST") - val patch = clazz as Class> - - // TODO: include declared classes from patch - - this.add(patch) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/util/patch/base/PatchBundle.kt b/src/main/kotlin/app/revanced/patcher/util/patch/base/PatchBundle.kt index e01bb02d..befc786f 100644 --- a/src/main/kotlin/app/revanced/patcher/util/patch/base/PatchBundle.kt +++ b/src/main/kotlin/app/revanced/patcher/util/patch/base/PatchBundle.kt @@ -1,5 +1,6 @@ package app.revanced.patcher.util.patch.base +import app.revanced.patcher.data.base.Data import app.revanced.patcher.patch.base.Patch import java.io.File @@ -11,7 +12,7 @@ abstract class PatchBundle(patchBundlePath: String) : File(patchBundlePath) { classNames.forEach { className -> val clazz = classLoader.loadClass(className) if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach - @Suppress("UNCHECKED_CAST") this.add(clazz as Class>) + @Suppress("UNCHECKED_CAST") this.add(clazz as Class>) } } } \ No newline at end of file