From 79d36401862eb1014be54c59a3c5a05a6a45b146 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 19 Nov 2025 00:20:41 +0100 Subject: [PATCH] feat: Modernize APIs --- gradle/libs.versions.toml | 2 +- .../app/revanced/patcher/Fingerprint.kt | 599 ------------------ .../kotlin/app/revanced/patcher/Matching.kt | 219 +++++++ .../mutable}/MutableAnnotation.kt | 4 +- .../mutable}/MutableAnnotationElement.kt | 8 +- .../mutable/MutableClassDef.kt} | 12 +- .../mutable}/MutableField.kt | 8 +- .../mutable}/MutableMethod.kt | 6 +- .../mutable}/MutableMethodParameter.kt | 4 +- .../MutableAnnotationEncodedValue.kt | 4 +- .../encodedValue/MutableArrayEncodedValue.kt | 4 +- .../MutableBooleanEncodedValue.kt | 2 +- .../encodedValue/MutableByteEncodedValue.kt | 2 +- .../encodedValue/MutableCharEncodedValue.kt | 2 +- .../encodedValue/MutableDoubleEncodedValue.kt | 2 +- .../encodedValue/MutableEncodedValue.kt | 2 +- .../encodedValue/MutableEnumEncodedValue.kt | 2 +- .../encodedValue/MutableFieldEncodedValue.kt | 2 +- .../encodedValue/MutableFloatEncodedValue.kt | 2 +- .../encodedValue/MutableIntEncodedValue.kt | 2 +- .../encodedValue/MutableLongEncodedValue.kt | 2 +- .../encodedValue/MutableMethodEncodedValue.kt | 2 +- .../MutableMethodHandleEncodedValue.kt | 2 +- .../MutableMethodTypeEncodedValue.kt | 2 +- .../encodedValue/MutableNullEncodedValue.kt | 2 +- .../encodedValue/MutableShortEncodedValue.kt | 2 +- .../encodedValue/MutableStringEncodedValue.kt | 2 +- .../encodedValue/MutableTypeEncodedValue.kt | 2 +- .../revanced/patcher/extensions/Extensions.kt | 11 - .../patcher/extensions/Instruction.kt | 14 + .../extensions/InstructionExtensions.kt | 434 ------------- .../app/revanced/patcher/extensions/Method.kt | 384 +++++++++++ .../patcher/patch/BytecodePatchContext.kt | 68 +- .../app/revanced/patcher/patch/Patch.kt | 49 +- .../app/revanced/patcher/util/ClassMerger.kt | 20 +- .../revanced/patcher/util/MethodNavigator.kt | 9 +- .../revanced/patcher/util/ProxyClassList.kt | 29 - .../revanced/patcher/util/proxy/ClassProxy.kt | 35 - .../patcher/util/smali/InlineSmaliCompiler.kt | 4 +- .../app/revanced/patcher/PatcherTest.kt | 11 +- .../extensions/InstructionExtensionsTest.kt | 12 +- .../util/smali/InlineSmaliCompilerTest.kt | 8 +- 42 files changed, 762 insertions(+), 1230 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patcher/Fingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patcher/Matching.kt rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/MutableAnnotation.kt (81%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/MutableAnnotationElement.kt (67%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes/MutableClass.kt => dex/mutable/MutableClassDef.kt} (85%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/MutableField.kt (83%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/MutableMethod.kt (90%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/MutableMethodParameter.kt (86%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableAnnotationEncodedValue.kt (84%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableArrayEncodedValue.kt (79%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableBooleanEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableByteEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableCharEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableDoubleEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableEncodedValue.kt (96%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableEnumEncodedValue.kt (90%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableFieldEncodedValue.kt (92%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableFloatEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableIntEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableLongEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableMethodEncodedValue.kt (90%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableMethodHandleEncodedValue.kt (91%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableMethodTypeEncodedValue.kt (91%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableNullEncodedValue.kt (84%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableShortEncodedValue.kt (89%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableStringEncodedValue.kt (90%) rename src/main/kotlin/app/revanced/patcher/{util/proxy/mutableTypes => dex/mutable}/encodedValue/MutableTypeEncodedValue.kt (89%) delete mode 100644 src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt create mode 100644 src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt delete mode 100644 src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt create mode 100644 src/main/kotlin/app/revanced/patcher/extensions/Method.kt delete mode 100644 src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt delete mode 100644 src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 058048c..03a9c2a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ android = "4.1.1.4" apktool-lib = "2.10.1.1" binary-compatibility-validator = "0.18.1" -kotlin = "2.0.20" +kotlin = "2.2.21" kotlinx-coroutines-core = "1.10.2" mockk = "1.14.5" multidexlib2 = "3.0.3.r3" diff --git a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt b/src/main/kotlin/app/revanced/patcher/Fingerprint.kt deleted file mode 100644 index a44c00b..0000000 --- a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt +++ /dev/null @@ -1,599 +0,0 @@ -@file:Suppress("unused", "MemberVisibilityCanBePrivate") - -package app.revanced.patcher - -import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull -import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.util.proxy.ClassProxy -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.ClassDef -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.StringReference -import com.android.tools.smali.dexlib2.util.MethodUtil - -/** - * A fingerprint for a method. A fingerprint is a partial description of a method. - * It is used to uniquely match a method by its characteristics. - * - * An example fingerprint for a public method that takes a single string parameter and returns void: - * ``` - * fingerprint { - * accessFlags(AccessFlags.PUBLIC) - * returns("V") - * parameters("Ljava/lang/String;") - * } - * ``` - * - * @param accessFlags The exact access flags using values of [AccessFlags]. - * @param returnType The return type. Compared using [String.startsWith]. - * @param parameters The parameters. Partial matches allowed and follow the same rules as [returnType]. - * @param opcodes A pattern of instruction opcodes. `null` can be used as a wildcard. - * @param strings A list of the strings. Compared using [String.contains]. - * @param custom A custom condition for this fingerprint. - * @param fuzzyPatternScanThreshold The threshold for fuzzy scanning the [opcodes] pattern. - */ -class Fingerprint internal constructor( - internal val accessFlags: Int?, - internal val returnType: String?, - internal val parameters: List?, - internal val opcodes: List?, - internal val strings: List?, - internal val custom: ((method: Method, classDef: ClassDef) -> Boolean)?, - private val fuzzyPatternScanThreshold: Int, -) { - @Suppress("ktlint:standard:backing-property-naming") - // Backing field needed for lazy initialization. - private var _matchOrNull: Match? = null - - /** - * The match for this [Fingerprint]. Null if unmatched. - */ - context(BytecodePatchContext) - private val matchOrNull: Match? - get() = matchOrNull() - - /** - * Match using [BytecodePatchContext.lookupMaps]. - * - * Generally faster than the other [matchOrNull] overloads when there are many methods to check for a match. - * - * Fingerprints can be optimized for performance: - * - Slowest: Specify [custom] or [opcodes] and nothing else. - * - Fast: Specify [accessFlags], [returnType]. - * - Faster: Specify [accessFlags], [returnType] and [parameters]. - * - Fastest: Specify [strings], with at least one string being an exact (non-partial) match. - * - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - */ - context(BytecodePatchContext) - internal fun matchOrNull(): Match? { - if (_matchOrNull != null) return _matchOrNull - - var match = strings?.mapNotNull { - lookupMaps.methodsByStrings[it] - }?.minByOrNull { it.size }?.let { methodClasses -> - methodClasses.forEach { (classDef, method) -> - val match = matchOrNull(classDef, method) - if (match != null) return@let match - } - - null - } - if (match != null) return match - - classes.forEach { classDef -> - match = matchOrNull(classDef) - if (match != null) return match - } - - return null - } - - /** - * Match using a [ClassDef]. - * - * @param classDef The class to match against. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - */ - context(BytecodePatchContext) - fun matchOrNull( - classDef: ClassDef, - ): Match? { - if (_matchOrNull != null) return _matchOrNull - - for (method in classDef.methods) { - val match = matchOrNull(method, classDef) - if (match != null) return match - } - - return null - } - - /** - * Match using a [Method]. - * The class is retrieved from the method. - * - * @param method The method to match against. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - */ - context(BytecodePatchContext) - fun matchOrNull( - method: Method, - ) = matchOrNull(method, classBy { method.definingClass == it.type }!!.immutableClass) - - /** - * Match using a [Method]. - * - * @param method The method to match against. - * @param classDef The class the method is a member of. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - */ - context(BytecodePatchContext) - fun matchOrNull( - method: Method, - classDef: ClassDef, - ): Match? { - if (_matchOrNull != null) return _matchOrNull - - if (returnType != null && !method.returnType.startsWith(returnType)) { - return null - } - - if (accessFlags != null && accessFlags != method.accessFlags) { - return null - } - - fun parametersEqual( - parameters1: Iterable, - parameters2: Iterable, - ): Boolean { - if (parameters1.count() != parameters2.count()) return false - val iterator1 = parameters1.iterator() - parameters2.forEach { - if (!it.startsWith(iterator1.next())) return false - } - return true - } - - // TODO: parseParameters() - if (parameters != null && !parametersEqual(parameters, method.parameterTypes)) { - return null - } - - if (custom != null && !custom.invoke(method, classDef)) { - return null - } - - val stringMatches: List? = - if (strings != null) { - buildList { - val instructions = method.instructionsOrNull ?: return null - - val stringsList = strings.toMutableList() - - instructions.forEachIndexed { instructionIndex, instruction -> - if ( - instruction.opcode != Opcode.CONST_STRING && - instruction.opcode != Opcode.CONST_STRING_JUMBO - ) { - return@forEachIndexed - } - - val string = ((instruction as ReferenceInstruction).reference as StringReference).string - val index = stringsList.indexOfFirst(string::contains) - if (index == -1) return@forEachIndexed - - add(Match.StringMatch(string, instructionIndex)) - stringsList.removeAt(index) - } - - if (stringsList.isNotEmpty()) return null - } - } else { - null - } - - val patternMatch = if (opcodes != null) { - val instructions = method.instructionsOrNull ?: return null - - fun patternScan(): Match.PatternMatch? { - val fingerprintFuzzyPatternScanThreshold = fuzzyPatternScanThreshold - - val instructionLength = instructions.count() - val patternLength = opcodes.size - - for (index in 0 until instructionLength) { - var patternIndex = 0 - var threshold = fingerprintFuzzyPatternScanThreshold - - while (index + patternIndex < instructionLength) { - val originalOpcode = instructions.elementAt(index + patternIndex).opcode - val patternOpcode = opcodes.elementAt(patternIndex) - - if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) { - // Reaching maximum threshold (0) means, - // the pattern does not match to the current instructions. - if (threshold-- == 0) break - } - - if (patternIndex < patternLength - 1) { - // If the entire pattern has not been scanned yet, continue the scan. - patternIndex++ - continue - } - - // The entire pattern has been scanned. - return Match.PatternMatch( - index, - index + patternIndex, - ) - } - } - - return null - } - - patternScan() ?: return null - } else { - null - } - - _matchOrNull = Match( - method, - patternMatch, - stringMatches, - classDef, - ) - - return _matchOrNull - } - - private val exception get() = PatchException("Failed to match the fingerprint: $this") - - /** - * The match for this [Fingerprint]. - * - * @throws PatchException If the [Fingerprint] has not been matched. - */ - context(BytecodePatchContext) - private val match - get() = matchOrNull ?: throw exception - - /** - * Match using a [ClassDef]. - * - * @param classDef The class to match against. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - fun match( - classDef: ClassDef, - ) = matchOrNull(classDef) ?: throw exception - - /** - * Match using a [Method]. - * The class is retrieved from the method. - * - * @param method The method to match against. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - fun match( - method: Method, - ) = matchOrNull(method) ?: throw exception - - /** - * Match using a [Method]. - * - * @param method The method to match against. - * @param classDef The class the method is a member of. - * @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise. - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - fun match( - method: Method, - classDef: ClassDef, - ) = matchOrNull(method, classDef) ?: throw exception - - /** - * The class the matching method is a member of. - */ - context(BytecodePatchContext) - val originalClassDefOrNull - get() = matchOrNull?.originalClassDef - - /** - * The matching method. - */ - context(BytecodePatchContext) - val originalMethodOrNull - get() = matchOrNull?.originalMethod - - /** - * The mutable version of [originalClassDefOrNull]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalClassDefOrNull] if mutable access is not required. - */ - context(BytecodePatchContext) - val classDefOrNull - get() = matchOrNull?.classDef - - /** - * The mutable version of [originalMethodOrNull]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalMethodOrNull] if mutable access is not required. - */ - context(BytecodePatchContext) - val methodOrNull - get() = matchOrNull?.method - - /** - * The match for the opcode pattern. - */ - context(BytecodePatchContext) - val patternMatchOrNull - get() = matchOrNull?.patternMatch - - /** - * The matches for the strings. - */ - context(BytecodePatchContext) - val stringMatchesOrNull - get() = matchOrNull?.stringMatches - - /** - * The class the matching method is a member of. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val originalClassDef - get() = match.originalClassDef - - /** - * The matching method. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val originalMethod - get() = match.originalMethod - - /** - * The mutable version of [originalClassDef]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalClassDef] if mutable access is not required. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val classDef - get() = match.classDef - - /** - * The mutable version of [originalMethod]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalMethod] if mutable access is not required. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val method - get() = match.method - - /** - * The match for the opcode pattern. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val patternMatch - get() = match.patternMatch - - /** - * The matches for the strings. - * - * @throws PatchException If the fingerprint has not been matched. - */ - context(BytecodePatchContext) - val stringMatches - get() = match.stringMatches -} - -/** - * A match of a [Fingerprint]. - * - * @param originalClassDef The class the matching method is a member of. - * @param originalMethod The matching method. - * @param patternMatch The match for the opcode pattern. - * @param stringMatches The matches for the strings. - */ -context(BytecodePatchContext) -class Match internal constructor( - val originalMethod: Method, - val patternMatch: PatternMatch?, - val stringMatches: List?, - val originalClassDef: ClassDef, -) { - /** - * The mutable version of [originalClassDef]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalClassDef] if mutable access is not required. - */ - val classDef by lazy { proxy(originalClassDef).mutableClass } - - /** - * The mutable version of [originalMethod]. - * - * Accessing this property allocates a [ClassProxy]. - * Use [originalMethod] if mutable access is not required. - */ - val method by lazy { classDef.methods.first { MethodUtil.methodSignaturesMatch(it, originalMethod) } } - - /** - * A match for an opcode pattern. - * @param startIndex The index of the first opcode of the pattern in the method. - * @param endIndex The index of the last opcode of the pattern in the method. - */ - class PatternMatch internal constructor( - val startIndex: Int, - val endIndex: Int, - ) - - /** - * A match for a string. - * - * @param string The string that matched. - * @param index The index of the instruction in the method. - */ - class StringMatch internal constructor(val string: String, val index: Int) -} - -/** - * A builder for [Fingerprint]. - * - * @property accessFlags The exact access flags using values of [AccessFlags]. - * @property returnType The return type compared using [String.startsWith]. - * @property parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType]. - * @property opcodes An opcode pattern of the instructions. Wildcard or unknown opcodes can be specified by `null`. - * @property strings A list of the strings compared each using [String.contains]. - * @property customBlock A custom condition for this fingerprint. - * @property fuzzyPatternScanThreshold The threshold for fuzzy pattern scanning. - * - * @constructor Create a new [FingerprintBuilder]. - */ -class FingerprintBuilder internal constructor( - private val fuzzyPatternScanThreshold: Int = 0, -) { - private var accessFlags: Int? = null - private var returnType: String? = null - private var parameters: List? = null - private var opcodes: List? = null - private var strings: List? = null - private var customBlock: ((method: Method, classDef: ClassDef) -> Boolean)? = null - - /** - * Set the access flags. - * - * @param accessFlags The exact access flags using values of [AccessFlags]. - */ - fun accessFlags(accessFlags: Int) { - this.accessFlags = accessFlags - } - - /** - * Set the access flags. - * - * @param accessFlags The exact access flags using values of [AccessFlags]. - */ - fun accessFlags(vararg accessFlags: AccessFlags) { - this.accessFlags = accessFlags.fold(0) { acc, it -> acc or it.value } - } - - /** - * Set the return type. - * - * @param returnType The return type compared using [String.startsWith]. - */ - fun returns(returnType: String) { - this.returnType = returnType - } - - /** - * Set the parameters. - * - * @param parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType]. - */ - fun parameters(vararg parameters: String) { - this.parameters = parameters.toList() - } - - /** - * Set the opcodes. - * - * @param opcodes An opcode pattern of instructions. - * Wildcard or unknown opcodes can be specified by `null`. - */ - fun opcodes(vararg opcodes: Opcode?) { - this.opcodes = opcodes.toList() - } - - /** - * Set the opcodes. - * - * @param instructions A list of instructions or opcode names in SMALI format. - * - Wildcard or unknown opcodes can be specified by `null`. - * - Empty lines are ignored. - * - Each instruction must be on a new line. - * - The opcode name is enough, no need to specify the operands. - * - * @throws Exception If an unknown opcode is used. - */ - fun opcodes(instructions: String) { - this.opcodes = instructions.trimIndent().split("\n").filter { - it.isNotBlank() - }.map { - // Remove any operands. - val name = it.split(" ", limit = 1).first().trim() - if (name == "null") return@map null - - opcodesByName[name] ?: throw Exception("Unknown opcode: $name") - } - } - - /** - * Set the strings. - * - * @param strings A list of strings compared each using [String.contains]. - */ - fun strings(vararg strings: String) { - this.strings = strings.toList() - } - - /** - * Set a custom condition for this fingerprint. - * - * @param customBlock A custom condition for this fingerprint. - */ - fun custom(customBlock: (method: Method, classDef: ClassDef) -> Boolean) { - this.customBlock = customBlock - } - - internal fun build() = Fingerprint( - accessFlags, - returnType, - parameters, - opcodes, - strings, - customBlock, - fuzzyPatternScanThreshold, - ) - - private companion object { - val opcodesByName = Opcode.entries.associateBy { it.name } - } -} - -/** - * Create a [Fingerprint]. - * - * @param fuzzyPatternScanThreshold The threshold for fuzzy pattern scanning. Default is 0. - * @param block The block to build the [Fingerprint]. - * - * @return The created [Fingerprint]. - */ -fun fingerprint( - fuzzyPatternScanThreshold: Int = 0, - block: FingerprintBuilder.() -> Unit, -) = FingerprintBuilder(fuzzyPatternScanThreshold).apply(block).build() diff --git a/src/main/kotlin/app/revanced/patcher/Matching.kt b/src/main/kotlin/app/revanced/patcher/Matching.kt new file mode 100644 index 0000000..7daa222 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/Matching.kt @@ -0,0 +1,219 @@ +@file:Suppress("unused", "MemberVisibilityCanBePrivate") + +package app.revanced.patcher + +import app.revanced.patcher.dex.mutable.MutableClassDef.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.gettingBytecodePatch +import com.android.tools.smali.dexlib2.HiddenApiRestriction +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Annotation +import com.android.tools.smali.dexlib2.iface.ClassDef +import com.android.tools.smali.dexlib2.iface.ExceptionHandler +import com.android.tools.smali.dexlib2.iface.Field +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.MethodImplementation +import com.android.tools.smali.dexlib2.iface.MethodParameter +import com.android.tools.smali.dexlib2.iface.TryBlock +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.util.InstructionUtil +import com.android.tools.smali.dexlib2.util.MethodUtil +import kotlin.collections.any +import kotlin.properties.ReadOnlyProperty + +fun Iterable.anyClassDef(predicate: ClassDef.() -> Boolean) = + any(predicate) + +fun ClassDef.anyMethod(predicate: Method.() -> Boolean) = + methods.any(predicate) + +fun ClassDef.anyDirectMethod(predicate: Method.() -> Boolean) = + directMethods.any(predicate) + +fun ClassDef.anyVirtualMethod(predicate: Method.() -> Boolean) = + virtualMethods.any(predicate) + +fun ClassDef.anyField(predicate: Field.() -> Boolean) = + fields.any(predicate) + +fun ClassDef.anyInstanceField(predicate: Field.() -> Boolean) = + instanceFields.any(predicate) + +fun ClassDef.anyStaticField(predicate: Field.() -> Boolean) = + staticFields.any(predicate) + +fun ClassDef.anyInterface(predicate: String.() -> Boolean) = + interfaces.any(predicate) + +fun ClassDef.anyAnnotation(predicate: Annotation.() -> Boolean) = + annotations.any(predicate) + +fun Method.implementation(predicate: MethodImplementation.() -> Boolean) = + implementation?.predicate() ?: false + +fun Method.anyParameter(predicate: MethodParameter.() -> Boolean) = + parameters.any(predicate) + +fun Method.anyParameterType(predicate: CharSequence.() -> Boolean) = + parameterTypes.any(predicate) + +fun Method.anyAnnotation(predicate: Annotation.() -> Boolean) = + annotations.any(predicate) + +fun Method.anyHiddenApiRestriction(predicate: HiddenApiRestriction.() -> Boolean) = + hiddenApiRestrictions.any(predicate) + +fun MethodImplementation.anyInstruction(predicate: Instruction.() -> Boolean) = + instructions.any(predicate) + +fun MethodImplementation.anyTryBlock(predicate: TryBlock.() -> Boolean) = + tryBlocks.any(predicate) + +fun MethodImplementation.anyDebugItem(predicate: Any.() -> Boolean) = + debugItems.any(predicate) + +fun Iterable.anyInstruction(predicate: Instruction.() -> Boolean) = + any(predicate) + + +fun BytecodePatchContext.firstClassDefOrNull(predicate: ClassDef.() -> Boolean) = + classDefs.firstOrNull { predicate(it) } + +fun BytecodePatchContext.firstClassDef(predicate: ClassDef.() -> Boolean) = + requireNotNull(firstClassDefOrNull(predicate)) + +fun BytecodePatchContext.firstMutableClassDefOrNull(predicate: ClassDef.() -> Boolean) = + firstClassDefOrNull(predicate)?.mutable() + +fun BytecodePatchContext.firstMutableClassDef(predicate: ClassDef.() -> Boolean) = + requireNotNull(firstMutableClassDefOrNull(predicate)) + +fun BytecodePatchContext.firstMethodOrNull(predicate: Method.() -> Boolean) = + classDefs.asSequence().flatMap { it.methods.asSequence() } + .firstOrNull { predicate(it) } + +fun BytecodePatchContext.firstMethod(predicate: Method.() -> Boolean) = + requireNotNull(firstMethodOrNull(predicate)) + +fun BytecodePatchContext.firstMutableMethodOrNull(predicate: Method.() -> Boolean): MutableMethod? { + classDefs.forEach { classDef -> + classDef.methods.forEach { method -> + if (predicate(method)) return classDef.mutable().methods.first { + MethodUtil.methodSignaturesMatch(it, method) + } + } + } + + return null +} + +fun BytecodePatchContext.firstMutableMethod(predicate: Method.() -> Boolean) = + requireNotNull(firstMutableMethodOrNull(predicate)) + +fun gettingFirstClassDefOrNull(predicate: ClassDef.() -> Boolean) = ReadOnlyProperty { thisRef, _ -> + require(thisRef is BytecodePatchContext) + + thisRef.firstClassDefOrNull(predicate) +} + +fun gettingFirstClassDef(predicate: ClassDef.() -> Boolean) = requireNotNull(gettingFirstClassDefOrNull(predicate)) + +fun gettingFirstMutableClassDefOrNull(predicate: ClassDef.() -> Boolean) = + ReadOnlyProperty { thisRef, _ -> + require(thisRef is BytecodePatchContext) + + thisRef.firstMutableClassDefOrNull(predicate) + } + +fun gettingFirstMutableClassDef(predicate: ClassDef.() -> Boolean) = + requireNotNull(gettingFirstMutableClassDefOrNull(predicate)) + +fun gettingFirstMethodOrNull(predicate: Method.() -> Boolean) = ReadOnlyProperty { thisRef, _ -> + require(thisRef is BytecodePatchContext) + + thisRef.firstMethodOrNull(predicate) +} + +fun gettingFirstMethod(predicate: Method.() -> Boolean) = requireNotNull(gettingFirstMethodOrNull(predicate)) + +fun gettingFirstMutableMethodOrNull(predicate: Method.() -> Boolean) = ReadOnlyProperty { thisRef, _ -> + require(thisRef is BytecodePatchContext) + + thisRef.firstMutableMethodOrNull(predicate) +} + +fun gettingFirstMutableMethod(predicate: Method.() -> Boolean) = + requireNotNull(gettingFirstMutableMethodOrNull(predicate)) + +val classDefOrNull by gettingFirstClassDefOrNull { true } +val classDef by gettingFirstClassDef { true } +val mutableClassDefOrNull by gettingFirstMutableClassDefOrNull { true } +val mutableClassDef by gettingFirstMutableClassDef { true } +val methodOrNull by gettingFirstMethodOrNull { true } +val methodDef by gettingFirstMethod { true } +val mutableMethodOrNull by gettingFirstMutableMethodOrNull { true } +val mutableMethodDef by gettingFirstMutableMethod { true } + +val `My Patch` by gettingBytecodePatch { + execute { + val classDefOrNull = firstClassDefOrNull { true } + val classDef = firstClassDef { true } + val mutableClassDefOrNull = firstMutableClassDefOrNull { true } + val mutableClassDef = firstMutableClassDef { true } + val methodOrNull = firstMethodOrNull { true } + val method = firstMethod { true } + val mutableMethodOrNull = firstMutableMethodOrNull { true } + val mutableMethod = firstMutableMethod { true } + val apiTest = firstMethod { + implementation { + instructions.matchSequentially { + add { opcode == Opcode.RETURN_VOID } + add { opcode == Opcode.NOP } + } + } + } + + fun Method.matchSequentiallyInstructions( + builder: MutableList Boolean>.() -> Unit + ) = implementation?.instructions?.matchSequentially(builder) ?: false + + firstMutableMethod { + matchSequentiallyInstructions { + add { opcode == Opcode.RETURN_VOID } + add { opcode == Opcode.NOP } + } + }.addInstructions("apiTest2") + } +} + + +abstract class Matcher : MutableList Boolean> by mutableListOf() { + var matchIndex = -1 + protected set + + abstract operator fun invoke(haystack: Iterable): Boolean +} + + +open class SequentialMatcher internal constructor() : Matcher() { + override operator fun invoke(haystack: Iterable) = true +} + +class CaptureStringIndices internal constructor() : Matcher() { + val capturedStrings = mutableMapOf() + + override operator fun invoke(haystack: Iterable) { + + } +} + +fun matchSequentially() = SequentialMatcher() +fun sequentialMatcher(builder: MutableList Boolean>.() -> Unit) = + SequentialMatcher().apply(builder) + +fun Iterable.matchSequentially(builder: MutableList Boolean>.() -> Unit) = + sequentialMatcher(builder)(this) diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotation.kt similarity index 81% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotation.kt index 49769fe..fc4228e 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotation.kt @@ -1,6 +1,6 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotationElement.Companion.toMutable import com.android.tools.smali.dexlib2.base.BaseAnnotation import com.android.tools.smali.dexlib2.iface.Annotation diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotationElement.kt similarity index 67% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotationElement.kt index 11261f5..db8f941 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableAnnotationElement.kt @@ -1,7 +1,9 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue.Companion.toMutable import com.android.tools.smali.dexlib2.base.BaseAnnotationElement import com.android.tools.smali.dexlib2.iface.AnnotationElement import com.android.tools.smali.dexlib2.iface.value.EncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableClassDef.kt similarity index 85% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableClassDef.kt index 88b33e5..8587e3c 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableClassDef.kt @@ -1,14 +1,14 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotation.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableField.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable import com.android.tools.smali.dexlib2.base.reference.BaseTypeReference import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.util.FieldUtil import com.android.tools.smali.dexlib2.util.MethodUtil -class MutableClass(classDef: ClassDef) : +class MutableClassDef(classDef: ClassDef) : BaseTypeReference(), ClassDef { // Class @@ -73,6 +73,6 @@ class MutableClass(classDef: ClassDef) : override fun getMethods(): MutableSet = _methods companion object { - fun ClassDef.toMutable(): MutableClass = MutableClass(this) + fun ClassDef.toMutable(): MutableClassDef = MutableClassDef(this) } } diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableField.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableField.kt similarity index 83% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableField.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableField.kt index 456c9b9..22d9014 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableField.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableField.kt @@ -1,8 +1,8 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotation.Companion.toMutable +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue.Companion.toMutable import com.android.tools.smali.dexlib2.HiddenApiRestriction import com.android.tools.smali.dexlib2.base.reference.BaseFieldReference import com.android.tools.smali.dexlib2.iface.Field diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethod.kt similarity index 90% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethod.kt index 5f0bb5b..00fdeb5 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethod.kt @@ -1,7 +1,7 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethodParameter.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotation.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableMethodParameter.Companion.toMutable import com.android.tools.smali.dexlib2.HiddenApiRestriction import com.android.tools.smali.dexlib2.base.reference.BaseMethodReference import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethodParameter.kt similarity index 86% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethodParameter.kt index 5bc0caa..57097fe 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/MutableMethodParameter.kt @@ -1,6 +1,6 @@ -package app.revanced.patcher.util.proxy.mutableTypes +package app.revanced.patcher.dex.mutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotation.Companion.toMutable import com.android.tools.smali.dexlib2.base.BaseMethodParameter import com.android.tools.smali.dexlib2.iface.MethodParameter diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableAnnotationEncodedValue.kt similarity index 84% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableAnnotationEncodedValue.kt index cddf148..9eb4e79 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableAnnotationEncodedValue.kt @@ -1,6 +1,6 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue -import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableAnnotationElement.Companion.toMutable import com.android.tools.smali.dexlib2.base.value.BaseAnnotationEncodedValue import com.android.tools.smali.dexlib2.iface.AnnotationElement import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableArrayEncodedValue.kt similarity index 79% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableArrayEncodedValue.kt index a4bb134..69b0664 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableArrayEncodedValue.kt @@ -1,6 +1,6 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.dex.mutable.encodedValue.MutableEncodedValue.Companion.toMutable import com.android.tools.smali.dexlib2.base.value.BaseArrayEncodedValue import com.android.tools.smali.dexlib2.iface.value.ArrayEncodedValue import com.android.tools.smali.dexlib2.iface.value.EncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableBooleanEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableBooleanEncodedValue.kt index 53bde5d..9293cc4 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableBooleanEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseBooleanEncodedValue import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableByteEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableByteEncodedValue.kt index b1d71ed..d9bdae4 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableByteEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseByteEncodedValue import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableCharEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableCharEncodedValue.kt index d2fe8f7..4f6a6d1 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableCharEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseCharEncodedValue import com.android.tools.smali.dexlib2.iface.value.CharEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableDoubleEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableDoubleEncodedValue.kt index 8358307..1af6dba 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableDoubleEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseDoubleEncodedValue import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEncodedValue.kt similarity index 96% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEncodedValue.kt index aa4cefa..9cc2a13 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.ValueType import com.android.tools.smali.dexlib2.iface.value.* diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEnumEncodedValue.kt similarity index 90% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEnumEncodedValue.kt index 27102ed..07b2a17 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableEnumEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseEnumEncodedValue import com.android.tools.smali.dexlib2.iface.reference.FieldReference diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFieldEncodedValue.kt similarity index 92% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFieldEncodedValue.kt index ca20e71..e56451d 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFieldEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.ValueType import com.android.tools.smali.dexlib2.base.value.BaseFieldEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFloatEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFloatEncodedValue.kt index e3c0aa4..5028ff1 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableFloatEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseFloatEncodedValue import com.android.tools.smali.dexlib2.iface.value.FloatEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableIntEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableIntEncodedValue.kt index a648ea6..67fad3f 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableIntEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseIntEncodedValue import com.android.tools.smali.dexlib2.iface.value.IntEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableLongEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableLongEncodedValue.kt index eb409fd..2db7a00 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableLongEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseLongEncodedValue import com.android.tools.smali.dexlib2.iface.value.LongEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodEncodedValue.kt similarity index 90% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodEncodedValue.kt index c1fbd27..4addacf 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseMethodEncodedValue import com.android.tools.smali.dexlib2.iface.reference.MethodReference diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodHandleEncodedValue.kt similarity index 91% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodHandleEncodedValue.kt index b982f8c..7c06402 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodHandleEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseMethodHandleEncodedValue import com.android.tools.smali.dexlib2.iface.reference.MethodHandleReference diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodTypeEncodedValue.kt similarity index 91% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodTypeEncodedValue.kt index 9d2f0d6..4ecec51 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableMethodTypeEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseMethodTypeEncodedValue import com.android.tools.smali.dexlib2.iface.reference.MethodProtoReference diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableNullEncodedValue.kt similarity index 84% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableNullEncodedValue.kt index 28f0208..25ca990 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableNullEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseNullEncodedValue import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableShortEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableShortEncodedValue.kt index f29d46a..3090778 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableShortEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseShortEncodedValue import com.android.tools.smali.dexlib2.iface.value.ShortEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableStringEncodedValue.kt similarity index 90% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableStringEncodedValue.kt index ea59637..001c60e 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableStringEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseStringEncodedValue import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableTypeEncodedValue.kt similarity index 89% rename from src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue.kt rename to src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableTypeEncodedValue.kt index 3e3c9ff..c058926 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/dex/mutable/encodedValue/MutableTypeEncodedValue.kt @@ -1,4 +1,4 @@ -package app.revanced.patcher.util.proxy.mutableTypes.encodedValue +package app.revanced.patcher.dex.mutable.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseTypeEncodedValue import com.android.tools.smali.dexlib2.iface.value.TypeEncodedValue diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt deleted file mode 100644 index 3be8f63..0000000 --- a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patcher.extensions - -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod - -/** - * Create a label for the instruction at given index. - * - * @param index The index to create the label for the instruction at. - * @return The label. - */ -fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index) diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt b/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt new file mode 100644 index 0000000..ecd8db2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt @@ -0,0 +1,14 @@ +package app.revanced.patcher.extensions + +import com.android.tools.smali.dexlib2.iface.instruction.DualReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.Reference + +@Suppress("UNCHECKED_CAST") +fun Instruction.reference(predicate: T.() -> Boolean) = + ((this as? ReferenceInstruction)?.reference as? T)?.predicate() ?: false + +@Suppress("UNCHECKED_CAST") +fun Instruction.reference2(predicate: T.() -> Boolean) = + ((this as? DualReferenceInstruction)?.reference2 as? T)?.predicate() ?: false diff --git a/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt deleted file mode 100644 index 315a851..0000000 --- a/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt +++ /dev/null @@ -1,434 +0,0 @@ -package app.revanced.patcher.extensions - -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patcher.util.smali.toInstruction -import app.revanced.patcher.util.smali.toInstructions -import com.android.tools.smali.dexlib2.builder.BuilderInstruction -import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction -import com.android.tools.smali.dexlib2.builder.Label -import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation -import com.android.tools.smali.dexlib2.builder.instruction.* -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.MethodImplementation -import com.android.tools.smali.dexlib2.iface.instruction.Instruction - -object InstructionExtensions { - /** - * Add instructions to a method at the given index. - * - * @param index The index to add the instructions at. - * @param instructions The instructions to add. - */ - fun MutableMethodImplementation.addInstructions( - index: Int, - instructions: List, - ) = instructions.asReversed().forEach { addInstruction(index, it) } - - /** - * Add instructions to a method. - * The instructions will be added at the end of the method. - * - * @param instructions The instructions to add. - */ - fun MutableMethodImplementation.addInstructions(instructions: List) = - instructions.forEach { addInstruction(it) } - - /** - * Remove instructions from a method at the given index. - * - * @param index The index to remove the instructions at. - * @param count The amount of instructions to remove. - */ - fun MutableMethodImplementation.removeInstructions( - index: Int, - count: Int, - ) = repeat(count) { - removeInstruction(index) - } - - /** - * Remove the first instructions from a method. - * - * @param count The amount of instructions to remove. - */ - fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count) - - /** - * Replace instructions at the given index with the given instructions. - * The amount of instructions to replace is the amount of instructions in the given list. - * - * @param index The index to replace the instructions at. - * @param instructions The instructions to replace the instructions with. - */ - fun MutableMethodImplementation.replaceInstructions( - index: Int, - instructions: List, - ) { - // Remove the instructions at the given index. - removeInstructions(index, instructions.size) - - // Add the instructions at the given index. - addInstructions(index, instructions) - } - - /** - * Add an instruction to a method at the given index. - * - * @param index The index to add the instruction at. - * @param instruction The instruction to add. - */ - fun MutableMethod.addInstruction( - index: Int, - instruction: BuilderInstruction, - ) = implementation!!.addInstruction(index, instruction) - - /** - * Add an instruction to a method. - * - * @param instruction The instructions to add. - */ - fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction) - - /** - * Add an instruction to a method at the given index. - * - * @param index The index to add the instruction at. - * @param smaliInstructions The instruction to add. - */ - fun MutableMethod.addInstruction( - index: Int, - smaliInstructions: String, - ) = implementation!!.addInstruction(index, smaliInstructions.toInstruction(this)) - - /** - * Add an instruction to a method. - * - * @param smaliInstructions The instruction to add. - */ - fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstruction(smaliInstructions.toInstruction(this)) - - /** - * Add instructions to a method at the given index. - * - * @param index The index to add the instructions at. - * @param instructions The instructions to add. - */ - fun MutableMethod.addInstructions( - index: Int, - instructions: List, - ) = implementation!!.addInstructions(index, instructions) - - /** - * Add instructions to a method. - * - * @param instructions The instructions to add. - */ - fun MutableMethod.addInstructions(instructions: List) = implementation!!.addInstructions(instructions) - - /** - * Add instructions to a method. - * - * @param smaliInstructions The instructions to add. - */ - fun MutableMethod.addInstructions( - index: Int, - smaliInstructions: String, - ) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this)) - - /** - * Add instructions to a method. - * - * @param smaliInstructions The instructions to add. - */ - fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this)) - - /** - * Add instructions to a method at the given index. - * - * @param index The index to add the instructions at. - * @param smaliInstructions The instructions to add. - * @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions]. - */ -// Special function for adding instructions with external labels. - fun MutableMethod.addInstructionsWithLabels( - index: Int, - smaliInstructions: String, - vararg externalLabels: ExternalLabel, - ) { - // Create reference dummy instructions for the instructions. - val nopSmali = - StringBuilder(smaliInstructions).also { builder -> - externalLabels.forEach { (name, _) -> - builder.append("\n:$name\nnop") - } - }.toString() - - // Compile the instructions with the dummy labels - val compiledInstructions = nopSmali.toInstructions(this) - - // Add the compiled list of instructions to the method. - addInstructions( - index, - compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size), - ) - - implementation!!.apply { - this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size) - .forEachIndexed { compiledInstructionIndex, compiledInstruction -> - // If the compiled instruction is not an offset instruction, skip it. - if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed - - /** - * Create a new label for the instruction - * and replace it with the label of the [compiledInstruction] at [compiledInstructionIndex]. - */ - fun Instruction.makeNewLabel() { - fun replaceOffset( - i: BuilderOffsetInstruction, - label: Label, - ): BuilderOffsetInstruction { - return when (i) { - is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label) - is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label) - is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label) - is BuilderInstruction22t -> - BuilderInstruction22t( - i.opcode, - i.registerA, - i.registerB, - label, - ) - is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label) - is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label) - else -> throw IllegalStateException( - "A non-offset instruction was given, this should never happen!", - ) - } - } - - // Create the final label. - val label = newLabelForIndex(this@apply.instructions.indexOf(this)) - - // Create the final instruction with the new label. - val newInstruction = - replaceOffset( - compiledInstruction, - label, - ) - - // Replace the instruction pointing to the dummy label - // with the new instruction pointing to the real instruction. - replaceInstruction(index + compiledInstructionIndex, newInstruction) - } - - // If the compiled instruction targets its own instruction, - // which means it points to some of its own, simply an offset has to be applied. - val labelIndex = compiledInstruction.target.location.index - if (labelIndex < compiledInstructions.size - externalLabels.size) { - // Get the targets index (insertion index + the index of the dummy instruction). - this.instructions[index + labelIndex].makeNewLabel() - return@forEachIndexed - } - - // Since the compiled instruction points to a dummy instruction, - // we can find the real instruction which it was created for by calculation. - - // Get the index of the instruction in the externalLabels list - // which the dummy instruction was created for. - // This works because we created the dummy instructions in the same order as the externalLabels list. - val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex] - instruction.makeNewLabel() - } - } - } - - /** - * Remove an instruction at the given index. - * - * @param index The index to remove the instruction at. - */ - fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index) - - /** - * Remove instructions at the given index. - * - * @param index The index to remove the instructions at. - * @param count The amount of instructions to remove. - */ - fun MutableMethod.removeInstructions( - index: Int, - count: Int, - ) = implementation!!.removeInstructions(index, count) - - /** - * Remove instructions at the given index. - * - * @param count The amount of instructions to remove. - */ - fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count) - - /** - * Replace an instruction at the given index. - * - * @param index The index to replace the instruction at. - * @param instruction The instruction to replace the instruction with. - */ - fun MutableMethod.replaceInstruction( - index: Int, - instruction: BuilderInstruction, - ) = implementation!!.replaceInstruction(index, instruction) - - /** - * Replace an instruction at the given index. - * - * @param index The index to replace the instruction at. - * @param smaliInstruction The smali instruction to replace the instruction with. - */ - fun MutableMethod.replaceInstruction( - index: Int, - smaliInstruction: String, - ) = implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this)) - - /** - * Replace instructions at the given index. - * - * @param index The index to replace the instructions at. - * @param instructions The instructions to replace the instructions with. - */ - fun MutableMethod.replaceInstructions( - index: Int, - instructions: List, - ) = implementation!!.replaceInstructions(index, instructions) - - /** - * Replace instructions at the given index. - * - * @param index The index to replace the instructions at. - * @param smaliInstructions The smali instructions to replace the instructions with. - */ - fun MutableMethod.replaceInstructions( - index: Int, - smaliInstructions: String, - ) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this)) - - /** - * Get an instruction at the given index. - * - * @param index The index to get the instruction at. - * @return The instruction. - */ - fun MethodImplementation.getInstruction(index: Int) = instructions.elementAt(index) - - /** - * Get an instruction at the given index. - * - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction. - */ - @Suppress("UNCHECKED_CAST") - fun MethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T - - /** - * Get an instruction at the given index. - * - * @param index The index to get the instruction at. - * @return The instruction. - */ - fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index] - - /** - * Get an instruction at the given index. - * - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction. - */ - @Suppress("UNCHECKED_CAST") - fun MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @return The instruction or null if the method has no implementation. - */ - fun Method.getInstructionOrNull(index: Int): Instruction? = implementation?.getInstruction(index) - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @return The instruction. - */ - fun Method.getInstruction(index: Int): Instruction = getInstructionOrNull(index)!! - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction or null if the method has no implementation. - */ - fun Method.getInstructionOrNull(index: Int): T? = implementation?.getInstruction(index) - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction. - */ - fun Method.getInstruction(index: Int): T = getInstructionOrNull(index)!! - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @return The instruction or null if the method has no implementation. - */ - fun MutableMethod.getInstructionOrNull(index: Int): BuilderInstruction? = implementation?.getInstruction(index) - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @return The instruction. - */ - fun MutableMethod.getInstruction(index: Int): BuilderInstruction = getInstructionOrNull(index)!! - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction or null if the method has no implementation. - */ - fun MutableMethod.getInstructionOrNull(index: Int): T? = implementation?.getInstruction(index) - - /** - * Get an instruction at the given index. - * @param index The index to get the instruction at. - * @param T The type of instruction to return. - * @return The instruction. - */ - fun MutableMethod.getInstruction(index: Int): T = getInstructionOrNull(index)!! - - /** - * The instructions of a method. - * @return The instructions or null if the method has no implementation. - */ - val Method.instructionsOrNull: Iterable? get() = implementation?.instructions - - /** - * The instructions of a method. - * @return The instructions. - */ - val Method.instructions: Iterable get() = instructionsOrNull!! - - /** - * The instructions of a method. - * @return The instructions or null if the method has no implementation. - */ - val MutableMethod.instructionsOrNull: MutableList? get() = implementation?.instructions - - /** - * The instructions of a method. - * @return The instructions. - */ - val MutableMethod.instructions: MutableList get() = instructionsOrNull!! -} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Method.kt b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt new file mode 100644 index 0000000..0076664 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt @@ -0,0 +1,384 @@ +package app.revanced.patcher.extensions + +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patcher.util.smali.toInstructions +import com.android.tools.smali.dexlib2.builder.BuilderInstruction +import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction +import com.android.tools.smali.dexlib2.builder.Label +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction10t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction20t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction30t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction31t +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.MethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import kotlin.collections.forEach +import kotlin.collections.indexOf + +/** + * Add instructions to a method at the given index. + * + * @param index The index to add the instructions at. + * @param instructions The instructions to add. + */ +fun MutableMethodImplementation.addInstructions( + index: Int, + instructions: List, +) = instructions.asReversed().forEach { addInstruction(index, it) } + +/** + * Add instructions to a method. + * The instructions will be added at the end of the method. + * + * @param instructions The instructions to add. + */ +fun MutableMethodImplementation.addInstructions(instructions: List) = + instructions.forEach { addInstruction(it) } + +/** + * Remove instructions from a method at the given index. + * + * @param index The index to remove the instructions at. + * @param count The amount of instructions to remove. + */ +fun MutableMethodImplementation.removeInstructions( + index: Int, + count: Int, +) = repeat(count) { + removeInstruction(index) +} + +/** + * Remove the first instructions from a method. + * + * @param count The amount of instructions to remove. + */ +fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count) + +/** + * Replace instructions at the given index with the given instructions. + * The amount of instructions to replace is the amount of instructions in the given list. + * + * @param index The index to replace the instructions at. + * @param instructions The instructions to replace the instructions with. + */ +fun MutableMethodImplementation.replaceInstructions( + index: Int, + instructions: List, +) { + // Remove the instructions at the given index. + removeInstructions(index, instructions.size) + + // Add the instructions at the given index. + addInstructions(index, instructions) +} + +/** + * Add instructions to a method at the given index. + * + * @param index The index to add the instructions at. + * @param instructions The instructions to add. + */ +fun MutableMethod.addInstructions( + index: Int, + instructions: List, +) = implementation!!.addInstructions(index, instructions) + +/** + * Add instructions to a method. + * + * @param instructions The instructions to add. + */ +fun MutableMethod.addInstructions(instructions: List) = + implementation!!.addInstructions(instructions) + +/** + * Add instructions to a method. + * + * @param smaliInstructions The instructions to add. + */ +fun MutableMethod.addInstructions( + index: Int, + smaliInstructions: String, +) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this)) + +/** + * Add instructions to a method. + * + * @param smaliInstructions The instructions to add. + */ +fun MutableMethod.addInstructions(smaliInstructions: String) = + implementation!!.addInstructions(smaliInstructions.toInstructions(this)) + +/** + * Add instructions to a method at the given index. + * + * @param index The index to add the instructions at. + * @param smaliInstructions The instructions to add. + * @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions]. + */ +// Special function for adding instructions with external labels. +fun MutableMethod.addInstructionsWithLabels( + index: Int, + smaliInstructions: String, + vararg externalLabels: ExternalLabel, +) { + // Create reference dummy instructions for the instructions. + val nopSmali = + StringBuilder(smaliInstructions).also { builder -> + externalLabels.forEach { (name, _) -> + builder.append("\n:$name\nnop") + } + }.toString() + + // Compile the instructions with the dummy labels + val compiledInstructions = nopSmali.toInstructions(this) + + // Add the compiled list of instructions to the method. + addInstructions( + index, + compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size), + ) + + implementation!!.apply { + this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size) + .forEachIndexed { compiledInstructionIndex, compiledInstruction -> + // If the compiled instruction is not an offset instruction, skip it. + if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed + + /** + * Create a new label for the instruction + * and replace it with the label of the [compiledInstruction] at [compiledInstructionIndex]. + */ + fun Instruction.makeNewLabel() { + fun replaceOffset( + i: BuilderOffsetInstruction, + label: Label, + ): BuilderOffsetInstruction { + return when (i) { + is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label) + is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label) + is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label) + is BuilderInstruction22t -> + BuilderInstruction22t( + i.opcode, + i.registerA, + i.registerB, + label, + ) + + is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label) + is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label) + else -> throw IllegalStateException( + "A non-offset instruction was given, this should never happen!", + ) + } + } + + // Create the final label. + val label = newLabelForIndex(this@apply.instructions.indexOf(this)) + + // Create the final instruction with the new label. + val newInstruction = + replaceOffset( + compiledInstruction, + label, + ) + + // Replace the instruction pointing to the dummy label + // with the new instruction pointing to the real instruction. + replaceInstruction(index + compiledInstructionIndex, newInstruction) + } + + // If the compiled instruction targets its own instruction, + // which means it points to some of its own, simply an offset has to be applied. + val labelIndex = compiledInstruction.target.location.index + if (labelIndex < compiledInstructions.size - externalLabels.size) { + // Get the targets index (insertion index + the index of the dummy instruction). + this.instructions[index + labelIndex].makeNewLabel() + return@forEachIndexed + } + + // Since the compiled instruction points to a dummy instruction, + // we can find the real instruction which it was created for by calculation. + + // Get the index of the instruction in the externalLabels list + // which the dummy instruction was created for. + // This works because we created the dummy instructions in the same order as the externalLabels list. + val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex] + instruction.makeNewLabel() + } + } +} + +/** + * Remove instructions at the given index. + * + * @param index The index to remove the instructions at. + * @param count The amount of instructions to remove. + */ +fun MutableMethod.removeInstructions( + index: Int, + count: Int, +) = implementation!!.removeInstructions(index, count) + +/** + * Remove instructions at the given index. + * + * @param count The amount of instructions to remove. + */ +fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count) + +/** + * Replace instructions at the given index. + * + * @param index The index to replace the instructions at. + * @param instructions The instructions to replace the instructions with. + */ +fun MutableMethod.replaceInstructions( + index: Int, + instructions: List, +) = implementation!!.replaceInstructions(index, instructions) + +/** + * Replace instructions at the given index. + * + * @param index The index to replace the instructions at. + * @param smaliInstructions The smali instructions to replace the instructions with. + */ +fun MutableMethod.replaceInstructions( + index: Int, + smaliInstructions: String, +) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this)) + +/** + * Get an instruction at the given index. + * + * @param index The index to get the instruction at. + * @return The instruction. + */ +fun MethodImplementation.getInstruction(index: Int) = instructions.elementAt(index) + +/** + * Get an instruction at the given index. + * + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction. + */ +@Suppress("UNCHECKED_CAST") +fun MethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T + +/** + * Get an instruction at the given index. + * + * @param index The index to get the instruction at. + * @return The instruction. + */ +fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index] + +/** + * Get an instruction at the given index. + * + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction. + */ +@Suppress("UNCHECKED_CAST") +fun MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @return The instruction or null if the method has no implementation. + */ +fun Method.getInstructionOrNull(index: Int): Instruction? = implementation?.getInstruction(index) + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @return The instruction. + */ +fun Method.getInstruction(index: Int): Instruction = getInstructionOrNull(index)!! + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction or null if the method has no implementation. + */ +fun Method.getInstructionOrNull(index: Int): T? = implementation?.getInstruction(index) + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction. + */ +fun Method.getInstruction(index: Int): T = getInstructionOrNull(index)!! + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @return The instruction or null if the method has no implementation. + */ +fun MutableMethod.getInstructionOrNull(index: Int): BuilderInstruction? = implementation?.getInstruction(index) + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @return The instruction. + */ +fun MutableMethod.getInstruction(index: Int): BuilderInstruction = getInstructionOrNull(index)!! + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction or null if the method has no implementation. + */ +fun MutableMethod.getInstructionOrNull(index: Int): T? = implementation?.getInstruction(index) + +/** + * Get an instruction at the given index. + * @param index The index to get the instruction at. + * @param T The type of instruction to return. + * @return The instruction. + */ +fun MutableMethod.getInstruction(index: Int): T = getInstructionOrNull(index)!! + +/** + * The instructions of a method. + * @return The instructions or null if the method has no implementation. + */ +val Method.instructionsOrNull: Iterable? get() = implementation?.instructions + +/** + * The instructions of a method. + * @return The instructions. + */ +val Method.instructions: Iterable get() = instructionsOrNull!! + +/** + * The instructions of a method. + * @return The instructions or null if the method has no implementation. + */ +val MutableMethod.instructionsOrNull: MutableList? get() = implementation?.instructions + +/** + * The instructions of a method. + * @return The instructions. + */ +val MutableMethod.instructions: MutableList get() = instructionsOrNull!! + +/** + * Create a label for the instruction at given index. + * + * @param index The index to create the label for the instruction at. + * @return The label. + */ +fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index) diff --git a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt index 5737481..86dc42e 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt @@ -3,11 +3,11 @@ package app.revanced.patcher.patch import app.revanced.patcher.InternalApi import app.revanced.patcher.PatcherConfig import app.revanced.patcher.PatcherResult -import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull +import app.revanced.patcher.dex.mutable.MutableClassDef +import app.revanced.patcher.dex.mutable.MutableClassDef.Companion.toMutable +import app.revanced.patcher.extensions.instructionsOrNull import app.revanced.patcher.util.ClassMerger.merge import app.revanced.patcher.util.MethodNavigator -import app.revanced.patcher.util.ProxyClassList -import app.revanced.patcher.util.proxy.ClassProxy import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcodes import com.android.tools.smali.dexlib2.iface.ClassDef @@ -21,7 +21,6 @@ import lanchon.multidexlib2.DexIO import lanchon.multidexlib2.MultiDexIO import lanchon.multidexlib2.RawDexIO import java.io.Closeable -import java.io.FileFilter import java.util.* import java.util.logging.Logger @@ -44,20 +43,18 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi /** * The list of classes. */ - val classes = ProxyClassList( - MultiDexIO.readDexFile( - true, - config.apkFile, - BasicDexFileNamer(), - null, - null, - ).also { opcodes = it.opcodes }.classes.toMutableList(), - ) + val classDefs = MultiDexIO.readDexFile( + true, + config.apkFile, + BasicDexFileNamer(), + null, + null, + ).also { opcodes = it.opcodes }.classes.toMutableSet() /** - * The lookup maps for methods and the class they are a member of from the [classes]. + * The lookup maps for methods and the class they are a member of from the [classDefs]. */ - internal val lookupMaps by lazy { LookupMaps(classes) } + internal val lookupMaps by lazy { LookupMaps(classDefs) } /** * Merge the extension of [bytecodePatch] into the [BytecodePatchContext]. @@ -71,7 +68,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi val existingClass = lookupMaps.classesByType[classDef.type] ?: run { logger.fine { "Adding class \"$classDef\"" } - classes += classDef + classDefs += classDef lookupMaps.classesByType[classDef.type] = classDef return@forEach @@ -85,32 +82,21 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi return@let } - classes -= existingClass - classes += mergedClass + classDefs -= existingClass + classDefs += mergedClass } } } ?: logger.fine("No extension to merge") } /** - * Find a class with a predicate. + * Convert a [ClassDef] to a [MutableClassDef]. + * If the [ClassDef] is already a [MutableClassDef], it is returned as is. * - * @param predicate A predicate to match the class. - * @return A proxy for the first class that matches the predicate. + * @return The mutable version of the [ClassDef]. */ - fun classBy(predicate: (ClassDef) -> Boolean) = - classes.proxyPool.find { predicate(it.immutableClass) } ?: classes.find(predicate)?.let { proxy(it) } - - /** - * Proxy the class to allow mutation. - * - * @param classDef The class to proxy. - * - * @return A proxy for the class. - */ - fun proxy(classDef: ClassDef) = classes.proxyPool.find { - it.immutableClass.type == classDef.type - } ?: ClassProxy(classDef).also { classes.proxyPool.add(it) } + fun ClassDef.mutable(): MutableClassDef = + this as? MutableClassDef ?: also(classDefs::remove).toMutable().also(classDefs::add) /** * Navigate a method. @@ -145,13 +131,13 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi BasicDexFileNamer(), object : DexFile { override fun getClasses() = - this@BytecodePatchContext.classes.also(ProxyClassList::replaceClasses).toSet() + this@BytecodePatchContext.classDefs.toSet() override fun getOpcodes() = this@BytecodePatchContext.opcodes }, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, ) { _, entryName, _ -> logger.info { "Compiled $entryName" } } - }.listFiles(FileFilter { it.isFile })!!.map { + }.listFiles { it.isFile }!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) }.toSet() @@ -163,9 +149,9 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi /** * A lookup map for methods and the class they are a member of and classes. * - * @param classes The list of classes to create the lookup maps from. + * @param classDefs The list of classes to create the lookup maps from. */ - internal class LookupMaps internal constructor(classes: List) : Closeable { + internal class LookupMaps internal constructor(classDefs: Set) : Closeable { /** * Methods associated by strings referenced in it. */ @@ -173,11 +159,11 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi // Lookup map for fast checking if a class exists by its type. val classesByType = mutableMapOf().apply { - classes.forEach { classDef -> put(classDef.type, classDef) } + classDefs.forEach { classDef -> put(classDef.type, classDef) } } init { - classes.forEach { classDef -> + classDefs.forEach { classDef -> classDef.methods.forEach { method -> val methodClassPair: MethodClassPair = method to classDef @@ -206,7 +192,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi override fun close() { lookupMaps.close() - classes.clear() + classDefs.clear() } } diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 39d27b3..22268df 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -15,6 +15,7 @@ import java.lang.reflect.Modifier import java.net.URLClassLoader import java.util.function.Supplier import java.util.jar.JarFile +import kotlin.properties.ReadOnlyProperty typealias PackageName = String typealias VersionName = String @@ -87,8 +88,7 @@ sealed class Patch>( finalizeBlock?.invoke(context) } - override fun toString() = name ?: - "Patch@${System.identityHashCode(this)}" + override fun toString() = name ?: "Patch@${System.identityHashCode(this)}" } internal fun Patch<*>.anyRecursively( @@ -437,6 +437,21 @@ fun bytecodePatch( block: BytecodePatchBuilder.() -> Unit = {}, ) = BytecodePatchBuilder(name, description, use).buildPatch(block) as BytecodePatch +/** + * Create a [ReadOnlyProperty] that creates a new [BytecodePatch] with the name of the property. + * + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * + * @return The created [ReadOnlyProperty] that creates a new [BytecodePatch]. + */ +fun gettingBytecodePatch( + description: String? = null, + use: Boolean = true, + block: BytecodePatchBuilder.() -> Unit = {}, +) = ReadOnlyProperty { _, property -> bytecodePatch(property.name, description, use, block) } + /** * A [RawResourcePatch] builder. * @@ -481,6 +496,21 @@ fun rawResourcePatch( block: RawResourcePatchBuilder.() -> Unit = {}, ) = RawResourcePatchBuilder(name, description, use).buildPatch(block) as RawResourcePatch +/** + * Create a [ReadOnlyProperty] that creates a new [RawResourcePatch] with the name of the property. + * + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * + * @return The created [ReadOnlyProperty] that creates a new [RawResourcePatch]. + */ +fun gettingRawResourcePatch( + description: String? = null, + use: Boolean = true, + block: RawResourcePatchBuilder.() -> Unit = {}, +) = ReadOnlyProperty { _, property -> rawResourcePatch(property.name, description, use, block) } + /** * A [ResourcePatch] builder. * @@ -526,6 +556,21 @@ fun resourcePatch( block: ResourcePatchBuilder.() -> Unit = {}, ) = ResourcePatchBuilder(name, description, use).buildPatch(block) as ResourcePatch +/** + * Create a [ReadOnlyProperty] that creates a new [ResourcePatch] with the name of the property. + * + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * + * @return The created [ReadOnlyProperty] that creates a new [ResourcePatch]. + */ +fun gettingResourcePatch( + description: String? = null, + use: Boolean = true, + block: ResourcePatchBuilder.() -> Unit = {}, +) = ReadOnlyProperty { _, property -> resourcePatch(property.name, description, use, block) } + /** * An exception thrown when patching. * diff --git a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt index d9a3a21..0a1ee25 100644 --- a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt +++ b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt @@ -7,12 +7,12 @@ import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny import app.revanced.patcher.util.ClassMerger.Utils.isPublic import app.revanced.patcher.util.ClassMerger.Utils.toPublic import app.revanced.patcher.util.ClassMerger.Utils.traverseClassHierarchy -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableField -import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableClassDef +import app.revanced.patcher.dex.mutable.MutableClassDef.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableField +import app.revanced.patcher.dex.mutable.MutableField.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.util.MethodUtil @@ -175,18 +175,18 @@ internal object ClassMerger { * @param callback function that is called for every class in the hierarchy */ fun BytecodePatchContext.traverseClassHierarchy( - targetClass: MutableClass, - callback: MutableClass.() -> Unit, + targetClass: MutableClassDef, + callback: MutableClassDef.() -> Unit, ) { callback(targetClass) targetClass.superclass ?: return - this.classBy { targetClass.superclass == it.type }?.mutableClass?.let { + classDefs.find { targetClass.superclass == it.type }?.mutable()?.let { traverseClassHierarchy(it, callback) } } - fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable() + fun ClassDef.asMutableClass() = if (this is MutableClassDef) this else this.toMutable() /** * Check if the [AccessFlags.PUBLIC] flag is set. diff --git a/src/main/kotlin/app/revanced/patcher/util/MethodNavigator.kt b/src/main/kotlin/app/revanced/patcher/util/MethodNavigator.kt index d894e9e..5c18432 100644 --- a/src/main/kotlin/app/revanced/patcher/util/MethodNavigator.kt +++ b/src/main/kotlin/app/revanced/patcher/util/MethodNavigator.kt @@ -2,10 +2,9 @@ package app.revanced.patcher.util -import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.util.MethodNavigator.NavigateException -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.extensions.instructionsOrNull import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction @@ -80,7 +79,7 @@ class MethodNavigator internal constructor( * * @return The last navigated method mutably. */ - fun stop() = classBy(matchesCurrentMethodReferenceDefiningClass)!!.mutableClass.firstMethodBySignature + fun stop() = classDefs.find(matchesCurrentMethodReferenceDefiningClass)!!.mutable().firstMethodBySignature as MutableMethod /** @@ -95,7 +94,7 @@ class MethodNavigator internal constructor( * * @return The last navigated method immutably. */ - fun original(): Method = classes.first(matchesCurrentMethodReferenceDefiningClass).firstMethodBySignature + fun original(): Method = classDefs.first(matchesCurrentMethodReferenceDefiningClass).firstMethodBySignature /** * Predicate to match the class defining the current method reference. diff --git a/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt b/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt deleted file mode 100644 index cdc334f..0000000 --- a/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt +++ /dev/null @@ -1,29 +0,0 @@ -package app.revanced.patcher.util - -import app.revanced.patcher.util.proxy.ClassProxy -import com.android.tools.smali.dexlib2.iface.ClassDef - -/** - * A list of classes and proxies. - * - * @param classes The classes to be backed by proxies. - */ -class ProxyClassList internal constructor(classes: MutableList) : MutableList by classes { - internal val proxyPool = mutableListOf() - - /** - * Replace all classes with their mutated versions. - */ - internal fun replaceClasses() = - proxyPool.removeIf { proxy -> - // If the proxy is unused, return false to keep it in the proxies list. - if (!proxy.resolved) return@removeIf false - - // If it has been used, replace the original class with the mutable class. - remove(proxy.immutableClass) - add(proxy.mutableClass) - - // Return true to remove the proxy from the proxies list. - return@removeIf true - } -} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt deleted file mode 100644 index ccd8abd..0000000 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt +++ /dev/null @@ -1,35 +0,0 @@ -package app.revanced.patcher.util.proxy - -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import com.android.tools.smali.dexlib2.iface.ClassDef - -/** - * A proxy class for a [ClassDef]. - * - * A class proxy simply holds a reference to the original class - * and allocates a mutable clone for the original class if needed. - * - * @param immutableClass The class to proxy. - */ -class ClassProxy internal constructor( - val immutableClass: ClassDef, -) { - /** - * Weather the proxy was actually used. - */ - internal var resolved = false - - /** - * The mutable clone of the original class. - * - * Note: This is only allocated if the proxy is actually used. - */ - val mutableClass by lazy { - resolved = true - if (immutableClass is MutableClass) { - immutableClass - } else { - MutableClass(immutableClass) - } - } -} diff --git a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt index 52af6bf..7e85e6b 100644 --- a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt +++ b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt @@ -1,7 +1,7 @@ package app.revanced.patcher.util.smali -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.extensions.instructions import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcodes import com.android.tools.smali.dexlib2.builder.BuilderInstruction diff --git a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt index bf4aab2..f291614 100644 --- a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt +++ b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt @@ -2,7 +2,6 @@ package app.revanced.patcher import app.revanced.patcher.patch.* import app.revanced.patcher.patch.BytecodePatchContext.LookupMaps -import app.revanced.patcher.util.ProxyClassList import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import io.mockk.* @@ -34,7 +33,7 @@ internal object PatcherTest { Logger.getAnonymousLogger(), ) - every { context.bytecodeContext.classes } returns mockk(relaxed = true) + every { context.bytecodeContext.classDefs } returns mockk(relaxed = true) every { this@mockk() } answers { callOriginal() } } } @@ -165,7 +164,7 @@ internal object PatcherTest { @Test fun `matches fingerprint`() { - every { patcher.context.bytecodeContext.classes } returns ProxyClassList( + every { patcher.context.bytecodeContext.classDefs } returns ProxyClassDefSet( mutableListOf( ImmutableClassDef( "class", @@ -198,8 +197,8 @@ internal object PatcherTest { val patches = setOf( bytecodePatch { execute { - fingerprint.match(classes.first().methods.first()) - fingerprint2.match(classes.first()) + fingerprint.match(classDefs.first().methods.first()) + fingerprint2.match(classDefs.first()) fingerprint3.originalClassDef } }, @@ -219,7 +218,7 @@ internal object PatcherTest { private operator fun Set>.invoke(): List { every { patcher.context.executablePatches } returns toMutableSet() - every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classes) + every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classDefs) every { with(patcher.context.bytecodeContext) { mergeExtension(any()) } } just runs return runBlocking { patcher().toList() } diff --git a/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt b/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt index 7185c84..c8c9382 100644 --- a/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt +++ b/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt @@ -1,15 +1,7 @@ package app.revanced.patcher.extensions -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable import app.revanced.patcher.util.smali.ExternalLabel import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode diff --git a/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt b/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt index c71b41a..d5740ae 100644 --- a/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt +++ b/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt @@ -1,10 +1,10 @@ package app.revanced.patcher.util.smali -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.extensions.newLabel -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.BuilderInstruction