From cd4a0a4dec89b52ef290685094da108efa502b24 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 21 Jan 2026 19:57:51 +0100 Subject: [PATCH] Use JvmName annotation to circumvent JVM signature clash and add unorderedAllOf function to match unordered using an indexedMatcher --- .../kotlin/app/revanced/patcher/Matching.kt | 1222 +++++++++-------- .../app/revanced/patcher/MatchingTest.kt | 105 +- 2 files changed, 664 insertions(+), 663 deletions(-) diff --git a/patcher/src/commonMain/kotlin/app/revanced/patcher/Matching.kt b/patcher/src/commonMain/kotlin/app/revanced/patcher/Matching.kt index 60ccfd6..b6b0377 100644 --- a/patcher/src/commonMain/kotlin/app/revanced/patcher/Matching.kt +++ b/patcher/src/commonMain/kotlin/app/revanced/patcher/Matching.kt @@ -2,18 +2,6 @@ package app.revanced.patcher -import app.revanced.patcher.BytecodePatchContextClassDefMatching.firstClassDefOrNull -import app.revanced.patcher.BytecodePatchContextClassDefMatching.firstMutableClassDefOrNull -import app.revanced.patcher.BytecodePatchContextMethodMatching.firstMutableMethodOrNull -import app.revanced.patcher.BytecodePatchContextMethodMatching.gettingFirstMethodDeclarativelyOrNull -import app.revanced.patcher.ClassDefMethodMatching.firstMethodDeclarativelyOrNull -import app.revanced.patcher.InstructionMatchingFunctions.invoke -import app.revanced.patcher.IterableClassDefClassDefMatching.firstClassDefOrNull -import app.revanced.patcher.IterableClassDefMethodMatching.firstMethodOrNull -import app.revanced.patcher.IterableMethodMethodMatching.firstMethodDeclarativelyOrNull -import app.revanced.patcher.IterableMethodMethodMatching.firstMethodOrNull -import app.revanced.patcher.IterableMethodMethodMatching.firstMutableMethodDeclarativelyOrNull -import app.revanced.patcher.IterableMethodMethodMatching.firstMutableMethodOrNull import app.revanced.patcher.extensions.* import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags @@ -107,526 +95,515 @@ private fun T.rememberDeclarativePredicate( predicate: DeclarativePredicate ) = rememberDeclarativePredicate("declarativePredicate") { predicate() } -object IterableMethodMethodMatching { - fun Iterable.firstMethodOrNull( - methodReference: MethodReference - ) = firstOrNull { MethodUtil.methodSignaturesMatch(methodReference, it) } +@JvmName("firstMethodOrNullInMethods") +fun Iterable.firstMethodOrNull( + methodReference: MethodReference +) = firstOrNull { MethodUtil.methodSignaturesMatch(methodReference, it) } - fun Iterable.firstMethod( - methodReference: MethodReference - ) = requireNotNull(firstMethodOrNull(methodReference)) +@JvmName("firstMethodInMethods") +fun Iterable.firstMethod( + methodReference: MethodReference +) = requireNotNull(firstMethodOrNull(methodReference)) - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodOrNull( - methodReference: MethodReference - ) = firstMethodOrNull(methodReference)?.let { BytecodePatchContextMethodMatching.firstMutableMethod(it) } +@JvmName("firstMutableMethodOrNullInMethods") +context(context: BytecodePatchContext) +fun Iterable.firstMutableMethodOrNull( + methodReference: MethodReference +) = firstMethodOrNull(methodReference)?.let { app.revanced.patcher.firstMutableMethod(it) } - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethod( - methodReference: MethodReference - ) = requireNotNull(firstMutableMethodOrNull(methodReference)) +@JvmName("firstMutableMethodInMethods") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethod( + methodReference: MethodReference +) = requireNotNull(firstMutableMethodOrNull(methodReference)) - fun Iterable.firstMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = if (strings.isEmpty()) withPredicateContext { firstOrNull { it.predicate() } } - else withPredicateContext { - first { method -> - val instructions = method.instructionsOrNull ?: return@first false +@JvmName("firstMethodOrNullInMethods") +fun Iterable.firstMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = if (strings.isEmpty()) withPredicateContext { firstOrNull { it.predicate() } } +else withPredicateContext { + first { method -> + val instructions = method.instructionsOrNull ?: return@first false - // TODO: Check potential to optimize (Set or not). - // Maybe even use context maps, but the methods may not be present in the context yet. - val methodStrings = instructions.asSequence().mapNotNull { it.string }.toSet() + // TODO: Check potential to optimize (Set or not). + // Maybe even use context maps, but the methods may not be present in the context yet. + val methodStrings = instructions.asSequence().mapNotNull { it.string }.toSet() - if (strings.any { it !in methodStrings }) return@first false + if (strings.any { it !in methodStrings }) return@first false - method.predicate() - } + method.predicate() } - - fun Iterable.firstMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) - - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = firstMethodOrNull( - strings = strings, predicate - )?.let { BytecodePatchContextMethodMatching.firstMutableMethod(it) } - - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) - - fun Iterable.firstMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - - fun Iterable.firstMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate - ) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) - - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate - ) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) } -object IterableClassDefMethodMatching { - fun Iterable.firstMethodOrNull( - methodReference: MethodReference - ) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMethodOrNull(methodReference) +@JvmName("firstMethodInMethods") +fun Iterable.firstMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) - fun Iterable.firstMethod( - methodReference: MethodReference - ) = requireNotNull(firstMethodOrNull(methodReference)) +@JvmName("firstMutableMethodOrNullInMethods") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = firstMethodOrNull( + strings = strings, predicate +)?.let { app.revanced.patcher.firstMutableMethod(it) } - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodOrNull( - methodReference: MethodReference - ) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMutableMethodOrNull(methodReference) +@JvmName("firstMutableMethodInMethods") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethod( - methodReference: MethodReference - ) = requireNotNull(firstMutableMethodOrNull(methodReference)) +@JvmName("firstMethodDeclarativelyOrNullInMethods") +fun Iterable.firstMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - fun Iterable.firstMethodOrNull( - predicate: MethodPredicate = { true }, - ) = asSequence().flatMap { it.methods.asSequence() }.asIterable() - .firstMethodOrNull(strings = emptyArray(), predicate) +@JvmName("firstMethodDeclarativelyInMethods") +fun Iterable.firstMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate +) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) - fun Iterable.firstMethod( - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMethodOrNull(predicate)) +@JvmName("firstMutableMethodDeclarativelyOrNullInMethods") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - fun Iterable.firstMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMethodOrNull(strings = strings, predicate) +@JvmName("firstMutableMethodDeclarativelyInMethods") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate +) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) - fun Iterable.firstMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = firstMethodOrNull( - strings = strings, predicate - )?.let { BytecodePatchContextMethodMatching.firstMutableMethod(it) } +@JvmName("firstMethodOrNullInClassDefs") +fun Iterable.firstMethodOrNull( + methodReference: MethodReference +) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMethodOrNull(methodReference) - context(_: BytecodePatchContext) - fun Iterable.firstMutableMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) +@JvmName("firstMethodInClassDefs") +fun Iterable.firstMethod( + methodReference: MethodReference +) = requireNotNull(firstMethodOrNull(methodReference)) - fun Iterable.firstMethodDeclarativelyOrNull( - predicate: DeclarativePredicate - ) = firstMethodOrNull { rememberDeclarativePredicate(predicate) } +@JvmName("firstMutableMethodOrNullInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethodOrNull( + methodReference: MethodReference +) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMutableMethodOrNull(methodReference) - fun Iterable.firstMethodDeclaratively( - predicate: DeclarativePredicate - ) = requireNotNull(firstMethodDeclarativelyOrNull(predicate)) +@JvmName("firstMutableMethodInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethod( + methodReference: MethodReference +) = requireNotNull(firstMutableMethodOrNull(methodReference)) - context(context: BytecodePatchContext) - fun Iterable.firstMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } +@JvmName("firstMethodOrNullInClassDefs") +fun Iterable.firstMethodOrNull( + predicate: MethodPredicate = { true }, +) = asSequence().flatMap { it.methods.asSequence() }.asIterable() + .firstMethodOrNull(strings = emptyArray(), predicate) - context(context: BytecodePatchContext) - fun Iterable.firstMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate - ) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) +@JvmName("firstMethodInClassDefs") +fun Iterable.firstMethod( + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMethodOrNull(predicate)) - context(context: BytecodePatchContext) - fun Iterable.firstMutableMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } +@JvmName("firstMethodOrNullInClassDefs") +fun Iterable.firstMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = asSequence().flatMap { it.methods.asSequence() }.asIterable().firstMethodOrNull(strings = strings, predicate) + +@JvmName("firstMethodInClassDefs") +fun Iterable.firstMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) + +@JvmName("firstMutableMethodOrNullInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = firstMethodOrNull( + strings = strings, predicate +)?.let { app.revanced.patcher.firstMutableMethod(it) } + +@JvmName("firstMutableMethodInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) + +@JvmName("firstMethodDeclarativelyOrNullInClassDefs") +fun Iterable.firstMethodDeclarativelyOrNull( + predicate: DeclarativePredicate +) = firstMethodOrNull { rememberDeclarativePredicate(predicate) } + +@JvmName("firstMethodDeclarativelyInClassDefs") +fun Iterable.firstMethodDeclaratively( + predicate: DeclarativePredicate +) = requireNotNull(firstMethodDeclarativelyOrNull(predicate)) + +@JvmName("firstMethodDeclarativelyOrNullInClassDefs") +context(context: BytecodePatchContext) +fun Iterable.firstMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } + +@JvmName("firstMethodDeclarativelyInClassDefs") +context(context: BytecodePatchContext) +fun Iterable.firstMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate +) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) + +@JvmName("firstMutableMethodDeclarativelyOrNullInClassDefs") +context(context: BytecodePatchContext) +fun Iterable.firstMutableMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } + +@JvmName("firstMethodOrNullInClassDef") +fun ClassDef.firstMethodOrNull( + methodReference: MethodReference +) = methods.firstMethodOrNull(methodReference) + +@JvmName("firstMethodInClassDef") +fun ClassDef.firstMethod( + methodReference: MethodReference +) = requireNotNull(firstMethodOrNull(methodReference)) + +@JvmName("firstMutableMethodOrNullInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethodOrNull( + methodReference: MethodReference +) = methods.firstMutableMethodOrNull(methodReference) + +@JvmName("firstMutableMethodInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethod( + methodReference: MethodReference +) = requireNotNull(firstMutableMethodOrNull(methodReference)) + +@JvmName("firstMethodOrNullInClassDef") +fun ClassDef.firstMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = methods.firstMethodOrNull(strings = strings, predicate) + +@JvmName("firstMethodInClassDef") +fun ClassDef.firstMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) + +@JvmName("firstMutableMethodOrNullInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = methods.firstMutableMethodOrNull(strings = strings, predicate) + +@JvmName("firstMutableMethodInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) + +@JvmName("firstMethodDeclarativelyOrNullInClassDef") +fun ClassDef.firstMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = methods.firstMethodDeclarativelyOrNull(strings = strings, predicate) + +@JvmName("firstMethodDeclarativelyInClassDef") +fun ClassDef.firstMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate +) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) + +@JvmName("firstMutableMethodDeclarativelyOrNullInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate +) = methods.firstMutableMethodDeclarativelyOrNull(strings = strings, predicate) + +@JvmName("firstMutableMethodDeclarativelyInClassDef") +context(_: BytecodePatchContext) +fun ClassDef.firstMutableMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate +) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) + +@JvmName("firstClassDefOrNullInClassDefs") +fun Iterable.firstClassDefOrNull( + type: String? = null, predicate: ClassDefPredicate = { true } +) = withPredicateContext { + if (type == null) firstOrNull { it.predicate() } + else firstOrNull { it.type == type && it.predicate() } } -object ClassDefMethodMatching { - fun ClassDef.firstMethodOrNull( - methodReference: MethodReference - ) = methods.firstMethodOrNull(methodReference) +@JvmName("firstClassDefInClassDefs") +fun Iterable.firstClassDef( + type: String? = null, predicate: ClassDefPredicate = { true } +) = requireNotNull(firstClassDefOrNull(type, predicate)) - fun ClassDef.firstMethod( - methodReference: MethodReference - ) = requireNotNull(firstMethodOrNull(methodReference)) +@JvmName("firstMutableClassDefOrNullInClassDefs") +context(context: BytecodePatchContext) +fun Iterable.firstMutableClassDefOrNull( + type: String? = null, predicate: ClassDefPredicate = { true } +) = if (type == null) firstClassDefOrNull(type, predicate) +else { + context.classDefs[type].takeIf { withPredicateContext { it?.predicate() == true } } +}?.let { context.classDefs.getOrReplaceMutable(it) } - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethodOrNull( - methodReference: MethodReference - ) = methods.firstMutableMethodOrNull(methodReference) +@JvmName("firstMutableClassDefInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableClassDef( + type: String? = null, predicate: ClassDefPredicate = { true } +) = requireNotNull(firstMutableClassDefOrNull(type, predicate)) - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethod( - methodReference: MethodReference - ) = requireNotNull(firstMutableMethodOrNull(methodReference)) +@JvmName("firstClassDefDeclarativelyOrNullInClassDefs") +fun Iterable.firstClassDefDeclarativelyOrNull( + type: String? = null, predicate: DeclarativePredicate +) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } - fun ClassDef.firstMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = methods.firstMethodOrNull(strings = strings, predicate) +@JvmName("firstClassDefDeclarativelyInClassDefs") +fun Iterable.firstClassDefDeclaratively( + type: String? = null, predicate: DeclarativePredicate +) = requireNotNull(firstClassDefDeclarativelyOrNull(type, predicate)) - fun ClassDef.firstMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) +@JvmName("firstMutableClassDefDeclarativelyOrNullInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableClassDefDeclarativelyOrNull( + type: String? = null, predicate: DeclarativePredicate +) = firstMutableClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = methods.firstMutableMethodOrNull(strings = strings, predicate) +@JvmName("firstMutableClassDefDeclarativelyInClassDefs") +context(_: BytecodePatchContext) +fun Iterable.firstMutableClassDefDeclaratively( + type: String? = null, predicate: DeclarativePredicate +) = requireNotNull(firstMutableClassDefDeclarativelyOrNull(type, predicate)) - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) - fun ClassDef.firstMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = methods.firstMethodDeclarativelyOrNull(strings = strings, predicate) +context(_: BytecodePatchContext) +fun firstMethodOrNull( + methodReference: MethodReference +) = firstClassDefOrNull(methodReference.definingClass)?.methods?.firstMethodOrNull(methodReference) - fun ClassDef.firstMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate - ) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) +context(_: BytecodePatchContext) +fun firstMethod( + method: MethodReference +) = requireNotNull(firstMethodOrNull(method)) - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate - ) = methods.firstMutableMethodDeclarativelyOrNull(strings = strings, predicate) - - context(_: BytecodePatchContext) - fun ClassDef.firstMutableMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate - ) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) +context(_: BytecodePatchContext) +fun firstMutableMethodOrNull( + methodReference: MethodReference +): MutableMethod? = firstMutableClassDefOrNull(methodReference.definingClass)?.methods?.first { + MethodUtil.methodSignaturesMatch( + methodReference, it + ) } -object IterableClassDefClassDefMatching { - fun Iterable.firstClassDefOrNull( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = withPredicateContext { - if (type == null) firstOrNull { it.predicate() } - else firstOrNull { it.type == type && it.predicate() } +context(_: BytecodePatchContext) +fun firstMutableMethod( + method: MethodReference +) = requireNotNull(firstMutableMethodOrNull(method)) + +context(context: BytecodePatchContext) +fun firstMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +): Method? = withPredicateContext { + if (strings.isEmpty()) return context.classDefs.firstMethodOrNull(predicate) + + // TODO: Get rid of duplicates, but this isn't needed for functionality. Perhaps worse performance-wise? + val strings = strings.toSet() + + val methodsWithStrings = strings.mapNotNull { context.classDefs.methodsByString[it] } + if (methodsWithStrings.size != strings.size) return null + + return methodsWithStrings.minBy { it.size }.firstOrNull { method -> + val containsAllOtherStrings = methodsWithStrings.all { method in it } + containsAllOtherStrings && method.predicate() } - - fun Iterable.firstClassDef( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = requireNotNull(firstClassDefOrNull(type, predicate)) - - context(context: BytecodePatchContext) - fun Iterable.firstMutableClassDefOrNull( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = if (type == null) firstClassDefOrNull(type, predicate) - else { - context.classDefs[type].takeIf { withPredicateContext { it?.predicate() == true } } - }?.let { context.classDefs.getOrReplaceMutable(it) } - - context(_: BytecodePatchContext) - fun Iterable.firstMutableClassDef( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = requireNotNull(firstMutableClassDefOrNull(type, predicate)) - - fun Iterable.firstClassDefDeclarativelyOrNull( - type: String? = null, predicate: DeclarativePredicate - ) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } - - fun Iterable.firstClassDefDeclaratively( - type: String? = null, predicate: DeclarativePredicate - ) = requireNotNull(firstClassDefDeclarativelyOrNull(type, predicate)) - - context(_: BytecodePatchContext) - fun Iterable.firstMutableClassDefDeclarativelyOrNull( - type: String? = null, predicate: DeclarativePredicate - ) = firstMutableClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } - - context(_: BytecodePatchContext) - fun Iterable.firstMutableClassDefDeclaratively( - type: String? = null, predicate: DeclarativePredicate - ) = requireNotNull(firstMutableClassDefDeclarativelyOrNull(type, predicate)) } -object BytecodePatchContextMethodMatching { - context(_: BytecodePatchContext) - fun firstMethodOrNull( - methodReference: MethodReference - ) = firstClassDefOrNull(methodReference.definingClass)?.methods?.firstMethodOrNull(methodReference) +context(_: BytecodePatchContext) +fun firstMethod( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) - context(_: BytecodePatchContext) - fun firstMethod( - method: MethodReference - ) = requireNotNull(firstMethodOrNull(method)) - - context(_: BytecodePatchContext) - fun firstMutableMethodOrNull( - methodReference: MethodReference - ): MutableMethod? = firstMutableClassDefOrNull(methodReference.definingClass)?.methods?.first { - MethodUtil.methodSignaturesMatch( - methodReference, it - ) - } - - context(_: BytecodePatchContext) - fun firstMutableMethod( - method: MethodReference - ) = requireNotNull(firstMutableMethodOrNull(method)) - - context(context: BytecodePatchContext) - fun firstMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ): Method? = withPredicateContext { - if (strings.isEmpty()) return context.classDefs.firstMethodOrNull(predicate) - - // TODO: Get rid of duplicates, but this isn't needed for functionality. Perhaps worse performance-wise? - val strings = strings.toSet() - - val methodsWithStrings = strings.mapNotNull { context.classDefs.methodsByString[it] } - if (methodsWithStrings.size != strings.size) return null - - return methodsWithStrings.minBy { it.size }.firstOrNull { method -> - val containsAllOtherStrings = methodsWithStrings.all { method in it } - containsAllOtherStrings && method.predicate() - } - } - - context(_: BytecodePatchContext) - fun firstMethod( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = requireNotNull(firstMethodOrNull(strings = strings, predicate)) - - context(_: BytecodePatchContext) - fun firstMutableMethodOrNull( - vararg strings: String, - predicate: MethodPredicate = { true }, - ) = firstMethodOrNull(strings = strings, predicate)?.let { method -> - firstMutableMethodOrNull(method) - } - - context(_: BytecodePatchContext) - fun firstMutableMethod( - vararg strings: String, predicate: MethodPredicate = { true } - ) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) - - fun gettingFirstMethodOrNull( - method: MethodReference - ) = cachedReadOnlyProperty { firstMethodOrNull(method) } - - fun gettingFirstMethod( - method: MethodReference - ) = cachedReadOnlyProperty { firstMethod(method) } - - fun gettingFirstMutableMethodOrNull( - method: MethodReference - ) = cachedReadOnlyProperty { firstMutableMethodOrNull(method) } - - fun gettingFirstMutableMethod( - method: MethodReference - ) = cachedReadOnlyProperty { firstMutableMethod(method) } - - fun gettingFirstMethodOrNull( - vararg strings: String, - predicate: BytecodePatchContextMethodPredicate = { true }, - ) = cachedReadOnlyProperty { firstMethodOrNull(strings = strings) { predicate() } } - - fun gettingFirstMethod( - vararg strings: String, - predicate: BytecodePatchContextMethodPredicate = { true }, - ) = cachedReadOnlyProperty { firstMethod(strings = strings) { predicate() } } - - fun gettingFirstMutableMethodOrNull( - vararg strings: String, - predicate: BytecodePatchContextMethodPredicate = { true }, - ) = cachedReadOnlyProperty { firstMutableMethodOrNull(strings = strings) { predicate() } } - - fun gettingFirstMutableMethod( - vararg strings: String, - predicate: BytecodePatchContextMethodPredicate = { true }, - ) = cachedReadOnlyProperty { firstMutableMethod(strings = strings) { predicate() } } - - context(_: BytecodePatchContext) - fun firstMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate = { } - ) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - - context(_: BytecodePatchContext) - fun firstMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate = { } - ) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) - - context(_: BytecodePatchContext) - fun firstMutableMethodDeclarativelyOrNull( - vararg strings: String, predicate: DeclarativePredicate = { } - ) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - - context(_: BytecodePatchContext) - fun firstMutableMethodDeclaratively( - vararg strings: String, predicate: DeclarativePredicate = { } - ) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) - - fun gettingFirstMethodDeclarativelyOrNull( - vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMethodOrNull(strings = strings) { rememberDeclarativePredicate { predicate() } } - - fun gettingFirstMethodDeclaratively( - vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMethod(strings = strings) { rememberDeclarativePredicate { predicate() } } - - fun gettingFirstMutableMethodDeclarativelyOrNull( - vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate { predicate() } } - - fun gettingFirstMutableMethodDeclaratively( - vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMutableMethod(strings = strings) { rememberDeclarativePredicate { predicate() } } +context(_: BytecodePatchContext) +fun firstMutableMethodOrNull( + vararg strings: String, + predicate: MethodPredicate = { true }, +) = firstMethodOrNull(strings = strings, predicate)?.let { method -> + firstMutableMethodOrNull(method) } -object BytecodePatchContextClassDefMatching { - context(context: BytecodePatchContext) - fun firstClassDefOrNull( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = if (type == null) context.classDefs.firstClassDefOrNull(type, predicate) - else withPredicateContext { context.classDefs[type]?.takeIf { it.predicate() } } +context(_: BytecodePatchContext) +fun firstMutableMethod( + vararg strings: String, predicate: MethodPredicate = { true } +) = requireNotNull(firstMutableMethodOrNull(strings = strings, predicate)) - context(_: BytecodePatchContext) - fun firstClassDef( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = requireNotNull(firstClassDefOrNull(type, predicate)) +fun gettingFirstMethodOrNull( + method: MethodReference +) = cachedReadOnlyProperty { firstMethodOrNull(method) } - context(context: BytecodePatchContext) - fun firstMutableClassDefOrNull( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = firstClassDefOrNull(type, predicate)?.let { context.classDefs.getOrReplaceMutable(it) } +fun gettingFirstMethod( + method: MethodReference +) = cachedReadOnlyProperty { firstMethod(method) } - context(_: BytecodePatchContext) - fun firstMutableClassDef( - type: String? = null, predicate: ClassDefPredicate = { true } - ) = requireNotNull(firstMutableClassDefOrNull(type, predicate)) +fun gettingFirstMutableMethodOrNull( + method: MethodReference +) = cachedReadOnlyProperty { firstMutableMethodOrNull(method) } - fun gettingFirstClassDefOrNull( - type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } - ) = cachedReadOnlyProperty { firstClassDefOrNull(type) { predicate() } } +fun gettingFirstMutableMethod( + method: MethodReference +) = cachedReadOnlyProperty { firstMutableMethod(method) } - fun gettingFirstClassDef( - type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } - ) = cachedReadOnlyProperty { firstClassDef(type) { predicate() } } +fun gettingFirstMethodOrNull( + vararg strings: String, + predicate: BytecodePatchContextMethodPredicate = { true }, +) = cachedReadOnlyProperty { firstMethodOrNull(strings = strings) { predicate() } } - fun gettingFirstMutableClassDefOrNull( - type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } - ) = cachedReadOnlyProperty { firstMutableClassDefOrNull(type) { predicate() } } +fun gettingFirstMethod( + vararg strings: String, + predicate: BytecodePatchContextMethodPredicate = { true }, +) = cachedReadOnlyProperty { firstMethod(strings = strings) { predicate() } } - fun gettingFirstMutableClassDef( - type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } - ) = cachedReadOnlyProperty { firstMutableClassDef(type) { predicate() } } +fun gettingFirstMutableMethodOrNull( + vararg strings: String, + predicate: BytecodePatchContextMethodPredicate = { true }, +) = cachedReadOnlyProperty { firstMutableMethodOrNull(strings = strings) { predicate() } } - context(_: BytecodePatchContext) - fun firstClassDefDeclarativelyOrNull( - type: String? = null, predicate: DeclarativePredicate = { } - ) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } +fun gettingFirstMutableMethod( + vararg strings: String, + predicate: BytecodePatchContextMethodPredicate = { true }, +) = cachedReadOnlyProperty { firstMutableMethod(strings = strings) { predicate() } } - context(_: BytecodePatchContext) - fun firstClassDefDeclaratively( - type: String? = null, predicate: DeclarativePredicate = { } - ) = firstClassDef(type) { rememberDeclarativePredicate(predicate) } +context(_: BytecodePatchContext) +fun firstMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate = { } +) = firstMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - context(_: BytecodePatchContext) - fun firstMutableClassDefDeclarativelyOrNull( - type: String? = null, predicate: DeclarativePredicate = { } - ) = firstMutableClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } +context(_: BytecodePatchContext) +fun firstMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate = { } +) = requireNotNull(firstMethodDeclarativelyOrNull(strings = strings, predicate)) - context(_: BytecodePatchContext) - fun firstMutableClassDefDeclaratively( - type: String? = null, predicate: DeclarativePredicate = { } - ) = firstMutableClassDef(type) { rememberDeclarativePredicate(predicate) } +context(_: BytecodePatchContext) +fun firstMutableMethodDeclarativelyOrNull( + vararg strings: String, predicate: DeclarativePredicate = { } +) = firstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate(predicate) } - fun gettingFirstClassDefDeclarativelyOrNull( - type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstClassDefOrNull { rememberDeclarativePredicate { predicate() } } +context(_: BytecodePatchContext) +fun firstMutableMethodDeclaratively( + vararg strings: String, predicate: DeclarativePredicate = { } +) = requireNotNull(firstMutableMethodDeclarativelyOrNull(strings = strings, predicate)) - fun gettingFirstClassDefDeclaratively( - type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstClassDef { rememberDeclarativePredicate { predicate() } } +fun gettingFirstMethodDeclarativelyOrNull( + vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMethodOrNull(strings = strings) { rememberDeclarativePredicate { predicate() } } - fun gettingFirstMutableClassDefDeclarativelyOrNull( - type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMutableClassDefOrNull { rememberDeclarativePredicate { predicate() } } +fun gettingFirstMethodDeclaratively( + vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMethod(strings = strings) { rememberDeclarativePredicate { predicate() } } - fun gettingFirstMutableClassDefDeclaratively( - type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } - ) = gettingFirstMutableClassDef { rememberDeclarativePredicate { predicate() } } +fun gettingFirstMutableMethodDeclarativelyOrNull( + vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMutableMethodOrNull(strings = strings) { rememberDeclarativePredicate { predicate() } } + +fun gettingFirstMutableMethodDeclaratively( + vararg strings: String, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMutableMethod(strings = strings) { rememberDeclarativePredicate { predicate() } } + + +context(context: BytecodePatchContext) +fun firstClassDefOrNull( + type: String? = null, predicate: ClassDefPredicate = { true } +) = if (type == null) context.classDefs.firstClassDefOrNull(type, predicate) +else withPredicateContext { context.classDefs[type]?.takeIf { it.predicate() } } + +context(_: BytecodePatchContext) +fun firstClassDef( + type: String? = null, predicate: ClassDefPredicate = { true } +) = requireNotNull(firstClassDefOrNull(type, predicate)) + +context(context: BytecodePatchContext) +fun firstMutableClassDefOrNull( + type: String? = null, predicate: ClassDefPredicate = { true } +) = firstClassDefOrNull(type, predicate)?.let { context.classDefs.getOrReplaceMutable(it) } + +context(_: BytecodePatchContext) +fun firstMutableClassDef( + type: String? = null, predicate: ClassDefPredicate = { true } +) = requireNotNull(firstMutableClassDefOrNull(type, predicate)) + +fun gettingFirstClassDefOrNull( + type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } +) = cachedReadOnlyProperty { firstClassDefOrNull(type) { predicate() } } + +fun gettingFirstClassDef( + type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } +) = cachedReadOnlyProperty { firstClassDef(type) { predicate() } } + +fun gettingFirstMutableClassDefOrNull( + type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } +) = cachedReadOnlyProperty { firstMutableClassDefOrNull(type) { predicate() } } + +fun gettingFirstMutableClassDef( + type: String? = null, predicate: BytecodePatchContextClassDefPredicate = { true } +) = cachedReadOnlyProperty { firstMutableClassDef(type) { predicate() } } + +context(_: BytecodePatchContext) +fun firstClassDefDeclarativelyOrNull( + type: String? = null, predicate: DeclarativePredicate = { } +) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } + +context(_: BytecodePatchContext) +fun firstClassDefDeclaratively( + type: String? = null, predicate: DeclarativePredicate = { } +) = firstClassDef(type) { rememberDeclarativePredicate(predicate) } + +context(_: BytecodePatchContext) +fun firstMutableClassDefDeclarativelyOrNull( + type: String? = null, predicate: DeclarativePredicate = { } +) = firstMutableClassDefOrNull(type) { rememberDeclarativePredicate(predicate) } + +context(_: BytecodePatchContext) +fun firstMutableClassDefDeclaratively( + type: String? = null, predicate: DeclarativePredicate = { } +) = firstMutableClassDef(type) { rememberDeclarativePredicate(predicate) } + +fun gettingFirstClassDefDeclarativelyOrNull( + type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstClassDefOrNull { rememberDeclarativePredicate { predicate() } } + +fun gettingFirstClassDefDeclaratively( + type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstClassDef { rememberDeclarativePredicate { predicate() } } + +fun gettingFirstMutableClassDefDeclarativelyOrNull( + type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMutableClassDefOrNull { rememberDeclarativePredicate { predicate() } } + +fun gettingFirstMutableClassDefDeclaratively( + type: String? = null, predicate: BytecodePatchContextDeclarativePredicate = { } +) = gettingFirstMutableClassDef { rememberDeclarativePredicate { predicate() } } -} class PredicateContext internal constructor() : MutableMap by mutableMapOf() private inline fun withPredicateContext(block: PredicateContext.() -> T) = PredicateContext().block() -typealias UnorderedMatcherPredicate = T.() -> U? - -fun unorderedMatcher(vararg predicates: UnorderedMatcherPredicate) = - UnorderedMatcher().apply { predicates.forEach { +it } } - -fun unorderedMatcher(build: Function>) = UnorderedMatcher().apply(build) - -fun Iterable.matchUnordered(build: Function>) = - unorderedMatcher(build)(this) - -context(_: PredicateContext) -fun Iterable.matchUnordered( - key: Any, vararg predicates: T.() -> U? -) = UnorderedMatcher()(key, this) { predicates.forEach { +it } } - -context(_: PredicateContext) -fun Iterable.rememberMatchUnordered(key: Any, build: Function>) = - unorderedMatcher(predicates = emptyArray())(key, this, build) - -class UnorderedMatcher : Matcher>() { - val indices: Map - field = mutableMapOf() - - override fun invoke(haystack: Iterable): Boolean { - indices.clear() - - val totalNeeded = size - if (totalNeeded == 0) return true - - val matchedPredicates = BooleanArray(totalNeeded) - var matchCount = 0 - - haystack.forEachIndexed { index, element -> - for (i in 0 until totalNeeded) { - if (matchedPredicates[i]) continue - - val result = this[i](element) ?: continue - - indices += result to index - matchedPredicates[i] = true - - if (++matchCount == totalNeeded) return true else break - } - } - - return false - } -} - typealias IndexedMatcherPredicate = T.(lastMatchedIndex: Int, currentIndex: Int, setNextIndex: (Int?) -> Unit) -> Boolean fun indexedMatcher(vararg items: IndexedMatcherPredicate) = IndexedMatcher().apply { @@ -671,12 +648,33 @@ fun after( fun after(range: IntRange = 1..1, predicate: Predicate) = after(range) { _, _, _ -> predicate() } -fun after(predicate: IndexedMatcherPredicate) = - after(1..1) { lastMatchedIndex, currentIndex, setNextIndex -> +fun after(predicate: IndexedMatcherPredicate) = after(1..1) { lastMatchedIndex, currentIndex, setNextIndex -> + predicate(lastMatchedIndex, currentIndex, setNextIndex) +} + +fun after(predicate: Predicate) = after { _, _, _ -> predicate() } + +fun afterAtLeast(steps: Int = 1, predicate: IndexedMatcherPredicate) = + after(steps..steps) { lastMatchedIndex, currentIndex, setNextIndex -> predicate(lastMatchedIndex, currentIndex, setNextIndex) } -fun after(predicate: Predicate) = after { _, _, _ -> predicate() } +fun afterAtLeast(steps: Int = 1, predicate: Predicate) = + after(steps..Int.MAX_VALUE) { _, _, _ -> predicate() } + +fun afterAtMost(steps: Int = 1, predicate: IndexedMatcherPredicate) = + after(1..steps) { lastMatchedIndex, currentIndex, setNextIndex -> + predicate(lastMatchedIndex, currentIndex, setNextIndex) + } + +fun afterAtMost(steps: Int = 1, predicate: Predicate) = after(1..steps) { _, _, _ -> predicate() } + +fun after(steps: Int = 1, predicate: IndexedMatcherPredicate) = + after(steps..steps) { lastMatchedIndex, currentIndex, setNextIndex -> + predicate(lastMatchedIndex, currentIndex, setNextIndex) + } + +fun after(steps: Int = 1, predicate: Predicate) = after(steps..steps) { _, _, _ -> predicate() } fun anyOf( vararg predicates: IndexedMatcherPredicate @@ -696,6 +694,41 @@ fun noneOf( predicates.none { predicate -> predicate(currentIndex, lastMatchedIndex, setNextIndex) } } +fun unorderedAllOf(vararg predicates: IndexedMatcherPredicate): Array> { + // Track which predicate index was used. + val usedPredicateIndices = mutableListOf() + var lastPatternIndex = -1 + + return (0 until predicates.size).map> { patternIndex -> + predicate@{ lastMatchedIndex, currentIndex, setNextIndex -> + // Detect backtracking: if revisiting an earlier pattern position, truncate the list to that position. + if (patternIndex <= lastPatternIndex) { + while (usedPredicateIndices.size > patternIndex) { + usedPredicateIndices.removeLast() + } + } + + lastPatternIndex = patternIndex + + // Try each unused predicate + for (predicateIndex in predicates.indices) { + if (predicateIndex in usedPredicateIndices) continue + + if (predicates[predicateIndex](lastMatchedIndex, currentIndex) { nextIndex -> + // Backtracking is not possible in an unordered matcher. + // If backtracking is requested, just set to max value to end searching. + if (nextIndex != -1) setNextIndex(Int.MAX_VALUE) + }) { + usedPredicateIndices += predicateIndex + return@predicate true + } + } + + false + } + }.toTypedArray() +} + class IndexedMatcher : Matcher>() { val indices: List field = mutableListOf() @@ -795,7 +828,6 @@ abstract class Matcher : MutableList by mutableListOf() { // region MutablePredicateList extensions - fun MutablePredicateList.allOf(block: Function>) { val child = MutablePredicateList().apply(block) add { child.all { it() } } @@ -823,7 +855,7 @@ fun MutablePredicateList.accessFlags(vararg flags: AccessFlags) = predic fun MutablePredicateList.returnType( predicate: Predicate -) = predicate { this.returnType.predicate() } +) = predicate { returnType.predicate() } fun MutablePredicateList.returnType( returnType: String, compare: String.(String) -> Boolean = String::startsWith @@ -831,7 +863,7 @@ fun MutablePredicateList.returnType( fun MutablePredicateList.name( predicate: Predicate -) = predicate { this.name.predicate() } +) = predicate { name.predicate() } fun MutablePredicateList.name( name: String, compare: String.(String) -> Boolean = String::equals @@ -839,7 +871,7 @@ fun MutablePredicateList.name( fun MutablePredicateList.definingClass( predicate: Predicate -) = predicate { this.definingClass.predicate() } +) = predicate { definingClass.predicate() } fun MutablePredicateList.definingClass( definingClass: String, compare: String.(String) -> Boolean = String::equals @@ -851,49 +883,46 @@ fun MutablePredicateList.parameterTypes(vararg parameterTypePrefixes: St } fun MutablePredicateList.strings( - build: Function> + build: Function> ) { - val match = unorderedMatcher(build) + val match = indexedMatcher(build) predicate { implementation { match(instructions) } } } -context(matcher: UnorderedMatcher) +context(matcher: IndexedMatcher) fun MutablePredicateList.strings( - build: Function> + build: Function> ) { matcher.build() predicate { implementation { matcher(instructions) } } } -fun MutablePredicateList.strings( - vararg predicates: UnorderedMatcherPredicate -) = strings { predicates.forEach { +it } } - -context(matcher: UnorderedMatcher) -fun MutablePredicateList.strings( - vararg predicates: UnorderedMatcherPredicate -) = strings { predicates.forEach { +it } } - -private fun Array.toUnorderedMatcherPredicate() = map { string -> - fun Instruction.(): String? { return string.takeIf { this.string == it } } -}.toTypedArray() - -fun MutablePredicateList.strings( - vararg strings: String -) = strings(predicates = strings.toUnorderedMatcherPredicate()) - -context( - stringsList: MutableList, - matcher: UnorderedMatcher) -fun MutablePredicateList.strings( - vararg strings: String -) { - stringsList += strings - - strings(predicates = strings.toUnorderedMatcherPredicate()) -} +//fun MutablePredicateList.strings( +// vararg predicates: IndexedMatcherPredicate +//) = strings { predicates.forEach { +it } } +// +//context(matcher: IndexedMatcher) +//fun MutablePredicateList.strings( +// vararg predicates: IndexedMatcherPredicate +//) = strings { predicates.forEach { +it } } +// +// +//fun MutablePredicateList.strings( +// vararg strings: String +//) = strings(predicates = strings.map { string(it) }.toTypedArray()) +// +//context( +// stringsList: MutableList, +// matcher: IndexedMatcherPredicate) +//fun MutablePredicateList.strings( +// vararg strings: String +//) { +// stringsList += strings +// +// strings(predicates = strings.map { string(it) }.toTypedArray()) +//} fun MutablePredicateList.instructions( build: Function> @@ -923,6 +952,10 @@ fun MutablePredicateList.instructions( vararg predicates: IndexedMatcherPredicate ) = instructions { predicates.forEach { +it } } +fun MutablePredicateList.instructions( + vararg predicates: Predicate +) = instructions { predicates.forEach { add { _, _, _ -> it() } } } + fun MutablePredicateList.custom(block: Predicate) { predicate { block() } } @@ -936,124 +969,103 @@ fun MutablePredicateList.opcodes( vararg opcodes: Opcode ) = instructions { opcodes.forEach { +it() } } -object InstructionMatchingFunctions { - inline fun `is`( - crossinline predicate: Predicate = { true } - ): IndexedMatcherPredicate = { _, _, _ -> (this as? T)?.predicate() == true } +inline fun `is`( + crossinline predicate: Predicate = { true } +): IndexedMatcherPredicate = { _, _, _ -> (this as? T)?.predicate() == true } - fun instruction(predicate: Predicate = { true }): IndexedMatcherPredicate = - { _, _, _ -> predicate() } +fun instruction(predicate: Predicate = { true }): IndexedMatcherPredicate = + { _, _, _ -> predicate() } - fun registers(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> - when (this) { - is RegisterRangeInstruction -> IntArray(registerCount) { startRegister + it }.predicate() +fun registers(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> + when (this) { + is RegisterRangeInstruction -> IntArray(registerCount) { startRegister + it }.predicate() - is FiveRegisterInstruction -> intArrayOf(registerC, registerD, registerE, registerF, registerG).predicate() + is FiveRegisterInstruction -> intArrayOf( + registerC, + registerD, + registerE, + registerF, + registerG + ).predicate() - is ThreeRegisterInstruction -> intArrayOf(registerA, registerB, registerC).predicate() + is ThreeRegisterInstruction -> intArrayOf(registerA, registerB, registerC).predicate() - is TwoRegisterInstruction -> intArrayOf(registerA, registerB).predicate() + is TwoRegisterInstruction -> intArrayOf(registerA, registerB).predicate() - is OneRegisterInstruction -> intArrayOf(registerA).predicate() + is OneRegisterInstruction -> intArrayOf(registerA).predicate() - else -> false - } + else -> false } - - fun registers( - vararg registers: Int, compare: IntArray.(registers: IntArray) -> Boolean = { registers -> - this.size >= registers.size && registers.indices.all { this[it] == registers[it] } - } - ) = registers({ compare(registers) }) - - fun literal(predicate: Predicate = { true }): IndexedMatcherPredicate = - { _, _, _ -> wideLiteral?.predicate() == true } - - fun literal(literal: Long, compare: Long.(Long) -> Boolean = Long::equals) = literal { compare(literal) } - - operator fun Long.invoke(compare: Long.(Long) -> Boolean = Long::equals) = literal(this, compare) - - inline fun reference( - crossinline predicate: Predicate = { true } - ): IndexedMatcherPredicate = { _, _, _ -> - (reference as? T)?.predicate() == true - } - - fun reference( - reference: String, compare: String.(String) -> Boolean = String::equals - ): IndexedMatcherPredicate = { _, _, _ -> this.reference?.toString()?.compare(reference) == true } - - fun field(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> - fieldReference?.predicate() == true - } - - fun field(name: String, compare: String.(String) -> Boolean = String::equals) = field { this.name.compare(name) } - - fun type(predicate: Predicate = { true }): IndexedMatcherPredicate = - { _, _, _ -> type?.predicate() == true } - - fun type(type: String, compare: String.(type: String) -> Boolean = String::equals) = type { compare(type) } - - fun method(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> - methodReference?.predicate() == true - } - - fun method(name: String, compare: String.(String) -> Boolean = String::equals) = method { this.name.compare(name) } - - fun string(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> - string?.predicate() == true - } - - context(stringsList: MutableList) - fun string( - string: String, - compare: String.(String) -> Boolean = String::equals - ): IndexedMatcherPredicate { - if (compare == String::equals) stringsList += string - - return string { compare(string) } - } - - fun string(string: String, compare: String.(String) -> Boolean = String::equals) = string { compare(string) } - - operator fun String.invoke(compare: String.(String) -> Boolean = String::equals) = string(this, compare) - - context(stringsList: MutableList) - operator fun String.invoke(compare: String.(String) -> Boolean = String::equals) = string(this, compare) - - operator fun Opcode.invoke(): IndexedMatcherPredicate = { _, _, _ -> opcode == this@invoke } } -object StringMatchingFunctions { - operator fun String.invoke( - compare: String.(String) -> Boolean = String::equals - ): UnorderedMatcherPredicate = { - this@invoke.takeIf { string?.compare(it) == true } +fun registers( + vararg registers: Int, compare: IntArray.(registers: IntArray) -> Boolean = { registers -> + this.size >= registers.size && registers.indices.all { this[it] == registers[it] } } +) = registers({ compare(registers) }) - context(stringsList: MutableList) - operator fun String.invoke( - compare: String.(String) -> Boolean = String::equals - ): UnorderedMatcherPredicate { - if (compare == String::equals) stringsList += this +fun literal(predicate: Predicate = { true }): IndexedMatcherPredicate = + { _, _, _ -> wideLiteral?.predicate() == true } - return { this@invoke.takeIf { string?.compare(it) == true } } - } +fun literal(literal: Long, compare: Long.(Long) -> Boolean = Long::equals) = literal { compare(literal) } - fun string(predicate: UnorderedMatcherPredicate) = predicate +operator fun Long.invoke(compare: Long.(Long) -> Boolean = Long::equals) = literal(this, compare) - context(stringsList: MutableList) - fun string(string: String, compare: String.(String) -> Boolean = String::equals) = string(compare) +inline fun reference( + crossinline predicate: Predicate = { true } +): IndexedMatcherPredicate = { _, _, _ -> (reference as? T)?.predicate() == true } - fun string(string: String, compare: String.(String) -> Boolean = String::equals) = string(compare) +fun reference( + reference: String, compare: String.(String) -> Boolean = String::equals +): IndexedMatcherPredicate = { _, _, _ -> this.reference?.toString()?.compare(reference) == true } + +fun field(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> + fieldReference?.predicate() == true } +fun field(name: String, compare: String.(String) -> Boolean = String::equals) = + field { this.name.compare(name) } + +fun type(predicate: Predicate = { true }): IndexedMatcherPredicate = + { _, _, _ -> type?.predicate() == true } + +fun type(type: String, compare: String.(type: String) -> Boolean = String::equals) = type { compare(type) } + +fun method(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> + methodReference?.predicate() == true +} + +fun method(name: String, compare: String.(String) -> Boolean = String::equals) = + method { this.name.compare(name) } + +fun string(predicate: Predicate = { true }): IndexedMatcherPredicate = { _, _, _ -> + string?.predicate() == true +} + +context(stringsList: MutableList) +fun string( + string: String, compare: String.(String) -> Boolean = String::equals +): IndexedMatcherPredicate { + if (compare == String::equals) stringsList += string + + return string { compare(string) } +} + +fun string(string: String, compare: String.(String) -> Boolean = String::equals) = string { compare(string) } + +operator fun String.invoke(compare: String.(String) -> Boolean = String::equals) = string(this, compare) + +context(stringsList: MutableList) +operator fun String.invoke(compare: String.(String) -> Boolean = String::equals) = string(this, compare) + +operator fun Opcode.invoke(): IndexedMatcherPredicate = { _, _, _ -> opcode == this@invoke } + + typealias BuildCompositeDeclarativePredicate = context( BytecodePatchContext, PredicateContext, IndexedMatcher, - UnorderedMatcher, MutableList ) MutablePredicateList.() -> Unit @@ -1065,7 +1077,6 @@ fun firstMethodComposite( class MatchBuilder private constructor( private val strings: MutableList, indexedMatcher: IndexedMatcher = indexedMatcher(), - stringMatcher: UnorderedMatcher = unorderedMatcher(), build: BuildCompositeDeclarativePredicate, ) { @@ -1074,11 +1085,10 @@ class MatchBuilder private constructor( ) : this(strings = mutableListOf(elements = strings), build = build) private val predicate: BytecodePatchContextDeclarativePredicate = { - context(strings, indexedMatcher, stringMatcher) { build() } + context(strings, indexedMatcher) { build() } } val indices = indexedMatcher.indices - val stringIndices = stringMatcher.indices private val BytecodePatchContext.cachedImmutableMethodOrNull by gettingFirstMethodDeclarativelyOrNull( strings = strings.toTypedArray(), predicate @@ -1124,10 +1134,7 @@ class MatchBuilder private constructor( context(context: BytecodePatchContext) fun match(classDef: ClassDef) = Match( - context, - classDef.firstMethodDeclarativelyOrNull { predicate() }, - indices, - stringIndices + context, classDef.firstMethodDeclarativelyOrNull { predicate() }, indices ) } @@ -1135,7 +1142,6 @@ class Match( val context: BytecodePatchContext, val immutableMethodOrNull: Method?, val indices: List, - val stringIndices: Map, ) { val immutableMethod by lazy { requireNotNull(immutableMethodOrNull) } diff --git a/patcher/src/jvmTest/kotlin/app/revanced/patcher/MatchingTest.kt b/patcher/src/jvmTest/kotlin/app/revanced/patcher/MatchingTest.kt index 429a843..706d786 100644 --- a/patcher/src/jvmTest/kotlin/app/revanced/patcher/MatchingTest.kt +++ b/patcher/src/jvmTest/kotlin/app/revanced/patcher/MatchingTest.kt @@ -1,14 +1,9 @@ package app.revanced.patcher -import app.revanced.patcher.BytecodePatchContextMethodMatching.firstMethod -import app.revanced.patcher.BytecodePatchContextMethodMatching.firstMethodDeclarativelyOrNull -import app.revanced.patcher.InstructionMatchingFunctions.invoke -import app.revanced.patcher.InstructionMatchingFunctions.`is` -import app.revanced.patcher.InstructionMatchingFunctions.registers -import app.revanced.patcher.InstructionMatchingFunctions.string -import app.revanced.patcher.InstructionMatchingFunctions.type +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNull @@ -18,7 +13,6 @@ import org.junit.jupiter.api.assertDoesNotThrow import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse -import kotlin.test.assertTrue @TestInstance(TestInstance.Lifecycle.PER_CLASS) class MatchingTest : PatcherTestBase() { @@ -33,9 +27,6 @@ class MatchingTest : PatcherTestBase() { if (fail) returnType("doesnt exist") - strings("This is a test.") - strings(StringMatchingFunctions.string("Hello", String::startsWith)) - instructions( at(0, Opcode.CONST_STRING()), `is`(), @@ -57,10 +48,6 @@ class MatchingTest : PatcherTestBase() { 4, match.indices[3], "Expected to find the string instruction at index 5" ) - assertEquals( - 0, match.stringIndices["Hello"], - "Expected to find 'Hello' at index 0" - ) assertNull( firstMethodComposite(fail = true).immutableMethodOrNull, @@ -102,45 +89,6 @@ class MatchingTest : PatcherTestBase() { } } - @Test - fun `unordered matcher works correctly`() { - val strings = listOf("apple", "banana", "cherry", "date", "elderberry") - val matcher = unorderedMatcher() - - matcher.apply { - add { "an".takeIf { contains(it) } } - add { "apple".takeIf { equals(it) } } - add { "elder".takeIf { startsWith(it) } } - } - assertTrue( - matcher(strings), - "Should match correctly" - ) - assertEquals( - matcher.indices["an"], 1, - "Should find 'banana' at index 1" - ) - assertEquals( - matcher.indices["apple"], 0, - "Should find 'apple' at index 0" - ) - assertEquals( - matcher.indices["elder"], 4, - "Should find 'elderberry' at index 4" - ) - matcher.clear() - - matcher.apply { - add { "xyz".takeIf { contains(it) } } - add { "apple".takeIf { equals(it) } } - add { "elder".takeIf { startsWith(it) } } - } - assertFalse( - matcher(strings), - "Should not match" - ) - } - @Test fun `indexed matcher finds indices correctly`() { val iterable = (1..10).toList() @@ -227,4 +175,51 @@ class MatchingTest : PatcherTestBase() { "Should match indices correctly." ) } -} \ No newline at end of file + + @Test + fun `unordered matching works correctly`() { + val list = bytecodePatchContext.classDefs.first().methods.first().instructions + val matcher = indexedMatcher() + + matcher.apply { + addAll( + unorderedAllOf( + afterAtLeast(1, Opcode.RETURN_OBJECT()), + string(), + Opcode.INVOKE_VIRTUAL(), + ) + ) + }(list) + assertEquals( + listOf(4, 5, 6), + matcher.indices, + "Should match because after(1) luckily only matches after the string at index 4." + ) + matcher.clear() + + matcher.apply { + addAll( + unorderedAllOf( + string("test", String::contains), + string("Hello", String::contains), + afterAtLeast(1, Opcode.RETURN_OBJECT()), + ) + ) + }(list) + assertEquals( + listOf(0, 4, 5), + matcher.indices, + "Should first match indices 4 due to the string, then after due to step 1, then the invoke." + ) + + assertFalse( + indexedMatcher( + items = unorderedAllOf( + { _, _, _ -> this == 1 }, + { _, _, _ -> this == -1 }, + after(2) { this == -2 } + ))(listOf(1, -1, 1, 2, -2)), + "Should not match because because 1 is matched at index 0, too early for after(2)." + ) + } +}