mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-11 13:56:16 +00:00
feat: Composition API
This commit is contained in:
@@ -48,7 +48,6 @@ class Fingerprint internal constructor(
|
|||||||
internal val strings: List<String>?,
|
internal val strings: List<String>?,
|
||||||
internal val custom: ((method: Method, classDef: ClassDef) -> Boolean)?,
|
internal val custom: ((method: Method, classDef: ClassDef) -> Boolean)?,
|
||||||
) {
|
) {
|
||||||
@Suppress("ktlint:standard:backing-property-naming")
|
|
||||||
// Backing field needed for lazy initialization.
|
// Backing field needed for lazy initialization.
|
||||||
private var _matchOrNull: Match? = null
|
private var _matchOrNull: Match? = null
|
||||||
|
|
||||||
@@ -117,7 +116,7 @@ class Fingerprint internal constructor(
|
|||||||
} else null
|
} else null
|
||||||
|
|
||||||
currentMethod = this
|
currentMethod = this
|
||||||
return filters == null || matchIndices(instructionsOrNull ?: return false, "match") {
|
return filters == null || matchIndices(instructionsOrNull ?: return false) {
|
||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
val filterMatches: Instruction.() -> Boolean = { filter.matches(currentMethod, this) }
|
val filterMatches: Instruction.() -> Boolean = { filter.matches(currentMethod, this) }
|
||||||
|
|
||||||
@@ -264,7 +263,7 @@ class Fingerprint internal constructor(
|
|||||||
|
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
return filters == null || matchIndices.apply {
|
return filters == null || matchIndices(instructionsOrNull ?: return false) {
|
||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
val filterMatches: Instruction.() -> Boolean = { filter.matches(method, this) }
|
val filterMatches: Instruction.() -> Boolean = { filter.matches(method, this) }
|
||||||
|
|
||||||
@@ -277,7 +276,7 @@ class Fingerprint internal constructor(
|
|||||||
is MatchFirst -> head { filterMatches() }
|
is MatchFirst -> head { filterMatches() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(instructionsOrNull ?: return false)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context(MatchContext()) { method.match() }) return null
|
if (!context(MatchContext()) { method.match() }) return null
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ package app.revanced.patcher
|
|||||||
|
|
||||||
import app.revanced.patcher.Matcher.MatchContext
|
import app.revanced.patcher.Matcher.MatchContext
|
||||||
import app.revanced.patcher.dex.mutable.MutableMethod
|
import app.revanced.patcher.dex.mutable.MutableMethod
|
||||||
|
import app.revanced.patcher.extensions.accessFlags
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
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.HiddenApiRestriction
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.*
|
import com.android.tools.smali.dexlib2.iface.*
|
||||||
import com.android.tools.smali.dexlib2.iface.Annotation
|
import com.android.tools.smali.dexlib2.iface.Annotation
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
@@ -217,7 +221,6 @@ fun gettingFirstMethodMutable(
|
|||||||
|
|
||||||
fun <T> indexedMatcher() = IndexedMatcher<T>()
|
fun <T> indexedMatcher() = IndexedMatcher<T>()
|
||||||
|
|
||||||
// Add lambda to emit instructions if matched (or matched arg)
|
|
||||||
fun <T> indexedMatcher(build: IndexedMatcher<T>.() -> Unit) =
|
fun <T> indexedMatcher(build: IndexedMatcher<T>.() -> Unit) =
|
||||||
IndexedMatcher<T>().apply(build)
|
IndexedMatcher<T>().apply(build)
|
||||||
|
|
||||||
@@ -234,7 +237,7 @@ operator fun <T> IndexedMatcher<T>.invoke(key: Any, iterable: Iterable<T>, build
|
|||||||
|
|
||||||
context(_: MatchContext)
|
context(_: MatchContext)
|
||||||
operator fun <T> IndexedMatcher<T>.invoke(iterable: Iterable<T>, builder: IndexedMatcher<T>.() -> Unit) =
|
operator fun <T> IndexedMatcher<T>.invoke(iterable: Iterable<T>, builder: IndexedMatcher<T>.() -> Unit) =
|
||||||
invoke(hashCode(), iterable, builder)
|
invoke(this@invoke.hashCode(), iterable, builder)
|
||||||
|
|
||||||
abstract class Matcher<T, U> : MutableList<U> by mutableListOf() {
|
abstract class Matcher<T, U> : MutableList<U> by mutableListOf() {
|
||||||
var matchIndex = -1
|
var matchIndex = -1
|
||||||
@@ -357,3 +360,245 @@ class IndexedMatcher<T>() : Matcher<T, T.(lastMatchedIndex: Int, currentIndex: I
|
|||||||
|
|
||||||
fun add(predicate: T.() -> Boolean) = add { _, _ -> predicate() }
|
fun add(predicate: T.() -> Boolean) = add { _, _ -> predicate() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeclarativePredicateBuilder<T> {
|
||||||
|
private val children = mutableListOf<T.() -> Boolean>()
|
||||||
|
|
||||||
|
fun anyOf(block: DeclarativePredicateBuilder<T>.() -> Unit) {
|
||||||
|
val child = DeclarativePredicateBuilder<T>().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> T.declarativePredicate(build: DeclarativePredicateBuilder<T>.() -> Unit) =
|
||||||
|
DeclarativePredicateBuilder<T>().apply(build).all(this)
|
||||||
|
|
||||||
|
context(_: MatchContext)
|
||||||
|
fun <T> T.rememberDeclarativePredicate(key: Any, block: DeclarativePredicateBuilder<T>.() -> Unit): Boolean =
|
||||||
|
remember(key) { DeclarativePredicateBuilder<T>().apply(block) }.all(this)
|
||||||
|
|
||||||
|
context(_: MatchContext)
|
||||||
|
private fun <T> T.rememberDeclarativePredicate(predicate: context(MatchContext, T) DeclarativePredicateBuilder<T>.() -> Unit) =
|
||||||
|
rememberDeclarativePredicate("declarative predicate build") { predicate() }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = firstClassDefOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(firstClassDefByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = firstClassDefMutableOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(firstClassDefMutableByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefByDeclarativePredicateOrNull(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = firstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefByDeclarativePredicate(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(firstClassDefByDeclarativePredicateOrNull(type, predicate))
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicateOrNull(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = firstClassDefMutableOrNull(type) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstClassDefMutableByDeclarativePredicate(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(firstClassDefMutableByDeclarativePredicateOrNull(type, predicate))
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstMethodByDeclarativePredicateOrNull(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = firstMethodOrNull(*strings) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstMethodByDeclarativePredicate(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(firstMethodByDeclarativePredicateOrNull(*strings, predicate = predicate))
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstMethodMutableByDeclarativePredicateOrNull(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = firstMethodMutableOrNull(*strings) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun BytecodePatchContext.firstMethodMutableByDeclarativePredicate(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(firstMethodMutableByDeclarativePredicateOrNull(*strings, predicate = predicate))
|
||||||
|
|
||||||
|
fun gettingFirstClassDefByDeclarativePredicateOrNull(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = gettingFirstClassDefOrNull(type) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstClassDefByDeclarativePredicate(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstClassDefByDeclarativePredicateOrNull(type, predicate))
|
||||||
|
|
||||||
|
fun gettingFirstClassDefMutableByDeclarativePredicateOrNull(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = gettingFirstClassDefMutableOrNull(type) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstClassDefMutableByDeclarativePredicate(
|
||||||
|
type: String,
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstClassDefMutableByDeclarativePredicateOrNull(type, predicate))
|
||||||
|
|
||||||
|
fun gettingFirstClassDefByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = gettingFirstClassDefOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstClassDefByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstClassDefByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun gettingFirstClassDefMutableByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = gettingFirstClassDefMutableOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstClassDefMutableByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, ClassDef) DeclarativePredicateBuilder<ClassDef>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstClassDefMutableByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun gettingFirstMethodByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = gettingFirstMethodOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstMethodByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstMethodByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun gettingFirstMethodMutableByDeclarativePredicateOrNull(
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = gettingFirstMethodMutableOrNull { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstMethodMutableByDeclarativePredicate(
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstMethodMutableByDeclarativePredicateOrNull(predicate))
|
||||||
|
|
||||||
|
fun gettingFirstMethodByDeclarativePredicateOrNull(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = gettingFirstMethodOrNull(*strings) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstMethodByDeclarativePredicate(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstMethodByDeclarativePredicateOrNull(*strings, predicate = predicate))
|
||||||
|
|
||||||
|
fun gettingFirstMethodMutableByDeclarativePredicateOrNull(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = gettingFirstMethodMutableOrNull(*strings) { rememberDeclarativePredicate(predicate) }
|
||||||
|
|
||||||
|
fun gettingFirstMethodMutableByDeclarativePredicate(
|
||||||
|
vararg strings: String,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> Unit
|
||||||
|
) = requireNotNull(gettingFirstMethodMutableByDeclarativePredicateOrNull(*strings, predicate = predicate))
|
||||||
|
|
||||||
|
|
||||||
|
class CompositionBuilder() {
|
||||||
|
val indexedMatcher = IndexedMatcher<Instruction>()
|
||||||
|
|
||||||
|
var accessFlagsPredicate: (DeclarativePredicateBuilder<Method>.() -> Unit)? = null
|
||||||
|
var returnsPredicate: (DeclarativePredicateBuilder<Method>.() -> Unit)? = null
|
||||||
|
var parameterTypesPredicate: (DeclarativePredicateBuilder<Method>.() -> Unit)? = null
|
||||||
|
var instructionsPredicate: (context(MatchContext) DeclarativePredicateBuilder<Method>.() -> Unit)? = null
|
||||||
|
var customPredicate: (context(MatchContext) DeclarativePredicateBuilder<Method>.() -> 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<Instruction>.() -> 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<MatchContext>()) {
|
||||||
|
instructionsPredicate?.invoke(this@with, this@Composition)
|
||||||
|
customPredicate?.invoke(this@with, this@Composition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Composition internal constructor(
|
||||||
|
private val indexedMatcher: IndexedMatcher<Instruction>,
|
||||||
|
predicate: context(MatchContext, Method) DeclarativePredicateBuilder<Method>.() -> 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;" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
package app.revanced.patcher.extensions
|
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.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.DualReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
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.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.*
|
import com.android.tools.smali.dexlib2.iface.reference.*
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
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 <reified T : Reference> Instruction.reference(predicate: T.() -> Boolean) =
|
inline fun <reified T : Reference> Instruction.reference(predicate: T.() -> Boolean) =
|
||||||
((this as? ReferenceInstruction)?.reference as? T)?.predicate() ?: false
|
((this as? ReferenceInstruction)?.reference as? T)?.predicate() ?: false
|
||||||
@@ -48,17 +60,72 @@ private inline fun <reified T : Reference> Instruction.reference(): T? =
|
|||||||
val Instruction.reference: Reference?
|
val Instruction.reference: Reference?
|
||||||
get() = reference()
|
get() = reference()
|
||||||
|
|
||||||
val Instruction.methodReference get() =
|
val Instruction.methodReference
|
||||||
|
get() =
|
||||||
reference<MethodReference>()
|
reference<MethodReference>()
|
||||||
|
|
||||||
val Instruction.fieldReference get() =
|
val Instruction.fieldReference
|
||||||
|
get() =
|
||||||
reference<FieldReference>()
|
reference<FieldReference>()
|
||||||
|
|
||||||
val Instruction.typeReference get() =
|
val Instruction.typeReference
|
||||||
|
get() =
|
||||||
reference<TypeReference>()
|
reference<TypeReference>()
|
||||||
|
|
||||||
val Instruction.stringReference get() =
|
val Instruction.stringReference
|
||||||
|
get() =
|
||||||
reference<StringReference>()
|
reference<StringReference>()
|
||||||
|
|
||||||
val Instruction.wideLiteral get() =
|
val Instruction.wideLiteral
|
||||||
|
get() =
|
||||||
(this as? WideLiteralInstruction)?.wideLiteral
|
(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<BuilderInstruction> {
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.dex.mutable.MutableMethod
|
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.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ typealias PackageName = String
|
|||||||
typealias VersionName = String
|
typealias VersionName = String
|
||||||
typealias Package = Pair<PackageName, Set<VersionName>?>
|
typealias Package = Pair<PackageName, Set<VersionName>?>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common interface for contexts such as [ResourcePatchContext] and [BytecodePatchContext].
|
||||||
|
*/
|
||||||
|
|
||||||
|
sealed interface PatchContext<T> : Supplier<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A patch.
|
* A patch.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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<T> : Supplier<T>
|
|
||||||
@@ -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<BuilderInstruction> {
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.toInstructions
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.util.toInstructions
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef
|
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef
|
||||||
@@ -298,7 +298,7 @@ internal object PatcherTest {
|
|||||||
val matchIndices = indexedMatcher<Instruction>()
|
val matchIndices = indexedMatcher<Instruction>()
|
||||||
val method by gettingFirstMethod {
|
val method by gettingFirstMethod {
|
||||||
implementation {
|
implementation {
|
||||||
matchIndices(instructions, "match") {
|
matchIndices(instructions) {
|
||||||
head { opcode == Opcode.CONST_STRING }
|
head { opcode == Opcode.CONST_STRING }
|
||||||
add { opcode == Opcode.IPUT_OBJECT }
|
add { opcode == Opcode.IPUT_OBJECT }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user