From 4106ce4070ff15fa9eaf6d102bb0e5cd11636441 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 28 Nov 2025 16:07:31 +0100 Subject: [PATCH] feat: Composition API --- .../app/revanced/patcher/Fingerprint.kt | 7 +- .../kotlin/app/revanced/patcher/Matching.kt | 249 +++++++++++++++++- .../patcher/extensions/Instruction.kt | 87 +++++- .../app/revanced/patcher/extensions/Method.kt | 1 - .../app/revanced/patcher/patch/Patch.kt | 7 + .../revanced/patcher/patch/PatchContext.kt | 9 - .../kotlin/app/revanced/patcher/util/Smali.kt | 64 ----- .../app/revanced/patcher/PatcherTest.kt | 4 +- 8 files changed, 336 insertions(+), 92 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patcher/patch/PatchContext.kt delete mode 100644 src/main/kotlin/app/revanced/patcher/util/Smali.kt diff --git a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt b/src/main/kotlin/app/revanced/patcher/Fingerprint.kt index 984431f..dae502c 100644 --- a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt +++ b/src/main/kotlin/app/revanced/patcher/Fingerprint.kt @@ -48,7 +48,6 @@ class Fingerprint internal constructor( internal val strings: List?, internal val custom: ((method: Method, classDef: ClassDef) -> Boolean)?, ) { - @Suppress("ktlint:standard:backing-property-naming") // Backing field needed for lazy initialization. private var _matchOrNull: Match? = null @@ -117,7 +116,7 @@ class Fingerprint internal constructor( } else null currentMethod = this - return filters == null || matchIndices(instructionsOrNull ?: return false, "match") { + return filters == null || matchIndices(instructionsOrNull ?: return false) { filters.forEach { filter -> val filterMatches: Instruction.() -> Boolean = { filter.matches(currentMethod, this) } @@ -264,7 +263,7 @@ class Fingerprint internal constructor( } else null - return filters == null || matchIndices.apply { + return filters == null || matchIndices(instructionsOrNull ?: return false) { filters.forEach { filter -> val filterMatches: Instruction.() -> Boolean = { filter.matches(method, this) } @@ -277,7 +276,7 @@ class Fingerprint internal constructor( is MatchFirst -> head { filterMatches() } } } - }(instructionsOrNull ?: return false) + } } if (!context(MatchContext()) { method.match() }) return null diff --git a/src/main/kotlin/app/revanced/patcher/Matching.kt b/src/main/kotlin/app/revanced/patcher/Matching.kt index 5c94cbf..3e56193 100644 --- a/src/main/kotlin/app/revanced/patcher/Matching.kt +++ b/src/main/kotlin/app/revanced/patcher/Matching.kt @@ -4,8 +4,12 @@ package app.revanced.patcher import app.revanced.patcher.Matcher.MatchContext import app.revanced.patcher.dex.mutable.MutableMethod +import app.revanced.patcher.extensions.accessFlags import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.gettingBytecodePatch +import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.HiddenApiRestriction +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.* import com.android.tools.smali.dexlib2.iface.Annotation import com.android.tools.smali.dexlib2.iface.instruction.Instruction @@ -217,7 +221,6 @@ fun gettingFirstMethodMutable( fun indexedMatcher() = IndexedMatcher() -// Add lambda to emit instructions if matched (or matched arg) fun indexedMatcher(build: IndexedMatcher.() -> Unit) = IndexedMatcher().apply(build) @@ -234,7 +237,7 @@ operator fun IndexedMatcher.invoke(key: Any, iterable: Iterable, build context(_: MatchContext) operator fun IndexedMatcher.invoke(iterable: Iterable, builder: IndexedMatcher.() -> Unit) = - invoke(hashCode(), iterable, builder) + invoke(this@invoke.hashCode(), iterable, builder) abstract class Matcher : MutableList by mutableListOf() { var matchIndex = -1 @@ -357,3 +360,245 @@ class IndexedMatcher() : Matcher Boolean) = add { _, _ -> predicate() } } + +class DeclarativePredicateBuilder { + private val children = mutableListOf Boolean>() + + fun anyOf(block: DeclarativePredicateBuilder.() -> Unit) { + val child = DeclarativePredicateBuilder().apply(block) + children += { child.children.any { it() } } + } + + fun predicate(block: T.() -> Boolean) { + children += block + } + + fun all(target: T): Boolean = children.all { target.it() } + fun any(target: T): Boolean = children.all { target.it() } +} + +fun T.declarativePredicate(build: DeclarativePredicateBuilder.() -> Unit) = + DeclarativePredicateBuilder().apply(build).all(this) + +context(_: MatchContext) +fun T.rememberDeclarativePredicate(key: Any, block: DeclarativePredicateBuilder.() -> Unit): Boolean = + remember(key) { DeclarativePredicateBuilder().apply(block) }.all(this) + +context(_: MatchContext) +private fun T.rememberDeclarativePredicate(predicate: context(MatchContext, T) DeclarativePredicateBuilder.() -> Unit) = + rememberDeclarativePredicate("declarative predicate build") { predicate() } + +fun BytecodePatchContext.firstClassDefByDeclarativePredicateOrNull( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = firstClassDefOrNull { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstClassDefByDeclarativePredicate( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstClassDefByDeclarativePredicateOrNull(predicate)) + +fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicateOrNull( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = firstClassDefMutableOrNull { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicate( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstClassDefMutableByDeclarativePredicateOrNull(predicate)) + +fun BytecodePatchContext.firstClassDefByDeclarativePredicateOrNull( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstClassDefByDeclarativePredicate( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstClassDefByDeclarativePredicateOrNull(type, predicate)) + +fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicateOrNull( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = firstClassDefMutableOrNull(type) { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicate( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstClassDefMutableByDeclarativePredicateOrNull(type, predicate)) + +fun BytecodePatchContext.firstMethodByDeclarativePredicateOrNull( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = firstMethodOrNull(*strings) { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstMethodByDeclarativePredicate( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstMethodByDeclarativePredicateOrNull(*strings, predicate = predicate)) + +fun BytecodePatchContext.firstMethodMutableByDeclarativePredicateOrNull( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = firstMethodMutableOrNull(*strings) { rememberDeclarativePredicate(predicate) } + +fun BytecodePatchContext.firstMethodMutableByDeclarativePredicate( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(firstMethodMutableByDeclarativePredicateOrNull(*strings, predicate = predicate)) + +fun gettingFirstClassDefByDeclarativePredicateOrNull( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } + +fun gettingFirstClassDefByDeclarativePredicate( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstClassDefByDeclarativePredicateOrNull(type, predicate)) + +fun gettingFirstClassDefMutableByDeclarativePredicateOrNull( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstClassDefMutableOrNull(type) { rememberDeclarativePredicate(predicate) } + +fun gettingFirstClassDefMutableByDeclarativePredicate( + type: String, + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstClassDefMutableByDeclarativePredicateOrNull(type, predicate)) + +fun gettingFirstClassDefByDeclarativePredicateOrNull( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstClassDefOrNull { rememberDeclarativePredicate(predicate) } + +fun gettingFirstClassDefByDeclarativePredicate( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstClassDefByDeclarativePredicateOrNull(predicate)) + +fun gettingFirstClassDefMutableByDeclarativePredicateOrNull( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstClassDefMutableOrNull { rememberDeclarativePredicate(predicate) } + +fun gettingFirstClassDefMutableByDeclarativePredicate( + predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstClassDefMutableByDeclarativePredicateOrNull(predicate)) + +fun gettingFirstMethodByDeclarativePredicateOrNull( + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstMethodOrNull { rememberDeclarativePredicate(predicate) } + +fun gettingFirstMethodByDeclarativePredicate( + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstMethodByDeclarativePredicateOrNull(predicate)) + +fun gettingFirstMethodMutableByDeclarativePredicateOrNull( + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstMethodMutableOrNull { rememberDeclarativePredicate(predicate) } + +fun gettingFirstMethodMutableByDeclarativePredicate( + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstMethodMutableByDeclarativePredicateOrNull(predicate)) + +fun gettingFirstMethodByDeclarativePredicateOrNull( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstMethodOrNull(*strings) { rememberDeclarativePredicate(predicate) } + +fun gettingFirstMethodByDeclarativePredicate( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstMethodByDeclarativePredicateOrNull(*strings, predicate = predicate)) + +fun gettingFirstMethodMutableByDeclarativePredicateOrNull( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = gettingFirstMethodMutableOrNull(*strings) { rememberDeclarativePredicate(predicate) } + +fun gettingFirstMethodMutableByDeclarativePredicate( + vararg strings: String, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) = requireNotNull(gettingFirstMethodMutableByDeclarativePredicateOrNull(*strings, predicate = predicate)) + + +class CompositionBuilder() { + val indexedMatcher = IndexedMatcher() + + var accessFlagsPredicate: (DeclarativePredicateBuilder.() -> Unit)? = null + var returnsPredicate: (DeclarativePredicateBuilder.() -> Unit)? = null + var parameterTypesPredicate: (DeclarativePredicateBuilder.() -> Unit)? = null + var instructionsPredicate: (context(MatchContext) DeclarativePredicateBuilder.() -> Unit)? = null + var customPredicate: (context(MatchContext) DeclarativePredicateBuilder.() -> Unit)? = null + + fun accessFlags(vararg flags: AccessFlags) { + accessFlagsPredicate = { predicate { accessFlags(*flags) } } + } + + fun returns(returnType: String) { + returnsPredicate = { predicate { this.returnType.startsWith(returnType) } } + } + + fun parameterTypes(vararg parameterTypes: String) { + parameterTypesPredicate = { + predicate { + this.parameterTypes.size == parameterTypes.size && this.parameterTypes.zip(parameterTypes) + .all { (a, b) -> a.startsWith(b) } + } + } + } + + fun instructions(build: context(MatchContext, Method) IndexedMatcher.() -> Unit) { + instructionsPredicate = { + predicate { + implementation { indexedMatcher(this@CompositionBuilder.hashCode(), instructions) { build() } } + } + } + } + + fun custom(block: context(MatchContext) Method.() -> Boolean) { + customPredicate = { predicate { block() } } + } + + + fun build() = Composition(indexedMatcher) { + accessFlagsPredicate?.invoke(this) + returnsPredicate?.invoke(this) + parameterTypesPredicate?.invoke(this) + with(contextOf()) { + instructionsPredicate?.invoke(this@with, this@Composition) + customPredicate?.invoke(this@with, this@Composition) + } + } +} + +class Composition internal constructor( + private val indexedMatcher: IndexedMatcher, + predicate: context(MatchContext, Method) DeclarativePredicateBuilder.() -> Unit +) { + val methodOrNull by gettingFirstMethodMutableByDeclarativePredicateOrNull(predicate) + val method = requireNotNull(methodOrNull) + val indices get() = indexedMatcher.indices +} + +fun composeFirstMethod(predicate: CompositionBuilder.() -> Unit) = CompositionBuilder().apply(predicate).build() + +val methodComposition = composeFirstMethod { + accessFlags(AccessFlags.PUBLIC) + instructions { + head { opcode == Opcode.RETURN } + fun opcode(opcode: Opcode) = add { this.opcode == opcode } + opcode(Opcode.NOP) + } +} + +val patch by gettingBytecodePatch { + execute { + val mutableMethod = methodComposition.method + methodComposition.indices.first() // Opcode == return + + firstClassDef(mutableMethod.definingClass).methods + firstClassDefMutable { type == mutableMethod.definingClass } + firstClassDefByDeclarativePredicateOrNull { + anyOf { + predicate { type == mutableMethod.definingClass } + predicate { type == "Ltest;" } + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt b/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt index 4c6034f..f16e030 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Instruction.kt @@ -1,12 +1,24 @@ package app.revanced.patcher.extensions +import app.revanced.patcher.dex.mutable.MutableMethod +import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.Opcodes +import com.android.tools.smali.dexlib2.builder.BuilderInstruction 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.instruction.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.reference.* import com.android.tools.smali.dexlib2.util.MethodUtil +import com.android.tools.smali.dexlib2.writer.builder.DexBuilder +import com.android.tools.smali.smali.smaliFlexLexer +import com.android.tools.smali.smali.smaliParser +import com.android.tools.smali.smali.smaliTreeWalker +import org.antlr.runtime.CommonTokenStream +import org.antlr.runtime.TokenSource +import org.antlr.runtime.tree.CommonTreeNodeStream +import java.io.StringReader inline fun Instruction.reference(predicate: T.() -> Boolean) = ((this as? ReferenceInstruction)?.reference as? T)?.predicate() ?: false @@ -48,17 +60,72 @@ private inline fun Instruction.reference(): T? = val Instruction.reference: Reference? get() = reference() -val Instruction.methodReference get() = - reference() +val Instruction.methodReference + get() = + reference() -val Instruction.fieldReference get() = - reference() +val Instruction.fieldReference + get() = + reference() -val Instruction.typeReference get() = - reference() +val Instruction.typeReference + get() = + reference() -val Instruction.stringReference get() = - reference() +val Instruction.stringReference + get() = + reference() -val Instruction.wideLiteral get() = - (this as? WideLiteralInstruction)?.wideLiteral +val Instruction.wideLiteral + get() = + (this as? WideLiteralInstruction)?.wideLiteral + + +private const val CLASS_HEADER = ".class LInlineCompiler;\n.super Ljava/lang/Object;\n" +private const val STATIC_HEADER = "$CLASS_HEADER.method public static dummyMethod(" +private const val HEADER = "$CLASS_HEADER.method public dummyMethod(" +private val sb by lazy { StringBuilder(512) } + +/** + * Compile lines of Smali code to a list of instructions. + * + * Note: Adding compiled instructions to an existing method with + * offset instructions WITHOUT specifying a parent method will not work. + * @param templateMethod The method to compile the instructions against. + * @returns A list of instructions. + */ +fun String.toInstructions(templateMethod: MutableMethod? = null): List { + val parameters = templateMethod?.parameterTypes?.joinToString("") { it } ?: "" + val registers = templateMethod?.implementation?.registerCount ?: 1 // TODO: Should this be 0? + val isStatic = templateMethod?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true + + sb.setLength(0) // reset + + if (isStatic) sb.append(STATIC_HEADER) else sb.append(HEADER) + sb.append(parameters).append(")V\n") + sb.append(" .registers ").append(registers).append("\n") + sb.append(trimIndent()).append("\n") + sb.append(".end method") + + val reader = StringReader(sb.toString()) + val lexer = smaliFlexLexer(reader, 15) + val tokens = CommonTokenStream(lexer as TokenSource) + val parser = smaliParser(tokens) + val fileTree = parser.smali_file() + + if (lexer.numberOfSyntaxErrors > 0 || parser.numberOfSyntaxErrors > 0) { + throw IllegalStateException( + "Lexer errors: ${lexer.numberOfSyntaxErrors}, Parser errors: ${parser.numberOfSyntaxErrors}" + ) + } + + val treeStream = CommonTreeNodeStream(fileTree.tree).apply { + tokenStream = tokens + } + + val walker = smaliTreeWalker(treeStream) + walker.setDexBuilder(DexBuilder(Opcodes.getDefault())) + + val classDef = walker.smali_file() + return classDef.methods.first().instructions.map { it as BuilderInstruction } +} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Method.kt b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt index 297d81c..3275d6b 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Method.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt @@ -1,7 +1,6 @@ package app.revanced.patcher.extensions import app.revanced.patcher.dex.mutable.MutableMethod -import app.revanced.patcher.util.toInstructions import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.builder.BuilderInstruction import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 22268df..1639ba8 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -21,6 +21,13 @@ typealias PackageName = String typealias VersionName = String typealias Package = Pair?> + +/** + * A common interface for contexts such as [ResourcePatchContext] and [BytecodePatchContext]. + */ + +sealed interface PatchContext : Supplier + /** * A patch. * diff --git a/src/main/kotlin/app/revanced/patcher/patch/PatchContext.kt b/src/main/kotlin/app/revanced/patcher/patch/PatchContext.kt deleted file mode 100644 index 33309a0..0000000 --- a/src/main/kotlin/app/revanced/patcher/patch/PatchContext.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patcher.patch - -import java.util.function.Supplier - -/** - * A common interface for contexts such as [ResourcePatchContext] and [BytecodePatchContext]. - */ - -sealed interface PatchContext : Supplier diff --git a/src/main/kotlin/app/revanced/patcher/util/Smali.kt b/src/main/kotlin/app/revanced/patcher/util/Smali.kt deleted file mode 100644 index 2dd4c05..0000000 --- a/src/main/kotlin/app/revanced/patcher/util/Smali.kt +++ /dev/null @@ -1,64 +0,0 @@ -package app.revanced.patcher.util - -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 -import com.android.tools.smali.dexlib2.writer.builder.DexBuilder -import com.android.tools.smali.smali.smaliFlexLexer -import com.android.tools.smali.smali.smaliParser -import com.android.tools.smali.smali.smaliTreeWalker -import org.antlr.runtime.CommonTokenStream -import org.antlr.runtime.TokenSource -import org.antlr.runtime.tree.CommonTreeNodeStream -import java.io.StringReader - -private const val CLASS_HEADER = ".class LInlineCompiler;\n.super Ljava/lang/Object;\n" -private const val STATIC_HEADER = "$CLASS_HEADER.method public static dummyMethod(" -private const val HEADER = "$CLASS_HEADER.method public dummyMethod(" -private val sb by lazy { StringBuilder(512) } - -/** - * Compile lines of Smali code to a list of instructions. - * - * Note: Adding compiled instructions to an existing method with - * offset instructions WITHOUT specifying a parent method will not work. - * @param templateMethod The method to compile the instructions against. - * @returns A list of instructions. - */ -fun String.toInstructions(templateMethod: MutableMethod? = null): List { - val parameters = templateMethod?.parameterTypes?.joinToString("") { it } ?: "" - val registers = templateMethod?.implementation?.registerCount ?: 1 // TODO: Should this be 0? - val isStatic = templateMethod?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true - - sb.setLength(0) // reset - - if (isStatic) sb.append(STATIC_HEADER) else sb.append(HEADER) - sb.append(parameters).append(")V\n") - sb.append(" .registers ").append(registers).append("\n") - sb.append(trimIndent()).append("\n") - sb.append(".end method") - - val reader = StringReader(sb.toString()) - val lexer = smaliFlexLexer(reader, 15) - val tokens = CommonTokenStream(lexer as TokenSource) - val parser = smaliParser(tokens) - val fileTree = parser.smali_file() - - if (lexer.numberOfSyntaxErrors > 0 || parser.numberOfSyntaxErrors > 0) { - throw IllegalStateException( - "Lexer errors: ${lexer.numberOfSyntaxErrors}, Parser errors: ${parser.numberOfSyntaxErrors}" - ) - } - - val treeStream = CommonTreeNodeStream(fileTree.tree).apply { - tokenStream = tokens - } - - val walker = smaliTreeWalker(treeStream) - walker.setDexBuilder(DexBuilder(Opcodes.getDefault())) - - val classDef = walker.smali_file() - return classDef.methods.first().instructions.map { it as BuilderInstruction } -} diff --git a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt index 64f57e9..0448960 100644 --- a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt +++ b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt @@ -1,7 +1,7 @@ package app.revanced.patcher +import app.revanced.patcher.extensions.toInstructions import app.revanced.patcher.patch.* -import app.revanced.patcher.util.toInstructions import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef @@ -298,7 +298,7 @@ internal object PatcherTest { val matchIndices = indexedMatcher() val method by gettingFirstMethod { implementation { - matchIndices(instructions, "match") { + matchIndices(instructions) { head { opcode == Opcode.CONST_STRING } add { opcode == Opcode.IPUT_OBJECT } }