mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-10 21:36: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 custom: ((method: Method, classDef: ClassDef) -> Boolean)?,
|
||||
) {
|
||||
@Suppress("ktlint:standard:backing-property-naming")
|
||||
// Backing field needed for lazy initialization.
|
||||
private var _matchOrNull: Match? = null
|
||||
|
||||
@@ -117,7 +116,7 @@ class Fingerprint internal constructor(
|
||||
} else null
|
||||
|
||||
currentMethod = this
|
||||
return filters == null || matchIndices(instructionsOrNull ?: return false, "match") {
|
||||
return filters == null || matchIndices(instructionsOrNull ?: return false) {
|
||||
filters.forEach { filter ->
|
||||
val filterMatches: Instruction.() -> Boolean = { filter.matches(currentMethod, this) }
|
||||
|
||||
@@ -264,7 +263,7 @@ class Fingerprint internal constructor(
|
||||
|
||||
} else null
|
||||
|
||||
return filters == null || matchIndices.apply {
|
||||
return filters == null || matchIndices(instructionsOrNull ?: return false) {
|
||||
filters.forEach { filter ->
|
||||
val filterMatches: Instruction.() -> Boolean = { filter.matches(method, this) }
|
||||
|
||||
@@ -277,7 +276,7 @@ class Fingerprint internal constructor(
|
||||
is MatchFirst -> head { filterMatches() }
|
||||
}
|
||||
}
|
||||
}(instructionsOrNull ?: return false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!context(MatchContext()) { method.match() }) return null
|
||||
|
||||
@@ -4,8 +4,12 @@ package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.Matcher.MatchContext
|
||||
import app.revanced.patcher.dex.mutable.MutableMethod
|
||||
import app.revanced.patcher.extensions.accessFlags
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.gettingBytecodePatch
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.HiddenApiRestriction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.*
|
||||
import com.android.tools.smali.dexlib2.iface.Annotation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
@@ -217,7 +221,6 @@ fun gettingFirstMethodMutable(
|
||||
|
||||
fun <T> indexedMatcher() = IndexedMatcher<T>()
|
||||
|
||||
// Add lambda to emit instructions if matched (or matched arg)
|
||||
fun <T> indexedMatcher(build: IndexedMatcher<T>.() -> Unit) =
|
||||
IndexedMatcher<T>().apply(build)
|
||||
|
||||
@@ -234,7 +237,7 @@ operator fun <T> IndexedMatcher<T>.invoke(key: Any, iterable: Iterable<T>, build
|
||||
|
||||
context(_: MatchContext)
|
||||
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() {
|
||||
var matchIndex = -1
|
||||
@@ -357,3 +360,245 @@ class IndexedMatcher<T>() : Matcher<T, T.(lastMatchedIndex: Int, currentIndex: I
|
||||
|
||||
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
|
||||
|
||||
import app.revanced.patcher.dex.mutable.MutableMethod
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.Opcodes
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.DualReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.*
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder
|
||||
import com.android.tools.smali.smali.smaliFlexLexer
|
||||
import com.android.tools.smali.smali.smaliParser
|
||||
import com.android.tools.smali.smali.smaliTreeWalker
|
||||
import org.antlr.runtime.CommonTokenStream
|
||||
import org.antlr.runtime.TokenSource
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream
|
||||
import java.io.StringReader
|
||||
|
||||
inline fun <reified T : Reference> Instruction.reference(predicate: T.() -> Boolean) =
|
||||
((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?
|
||||
get() = reference()
|
||||
|
||||
val Instruction.methodReference get() =
|
||||
reference<MethodReference>()
|
||||
val Instruction.methodReference
|
||||
get() =
|
||||
reference<MethodReference>()
|
||||
|
||||
val Instruction.fieldReference get() =
|
||||
reference<FieldReference>()
|
||||
val Instruction.fieldReference
|
||||
get() =
|
||||
reference<FieldReference>()
|
||||
|
||||
val Instruction.typeReference get() =
|
||||
reference<TypeReference>()
|
||||
val Instruction.typeReference
|
||||
get() =
|
||||
reference<TypeReference>()
|
||||
|
||||
val Instruction.stringReference get() =
|
||||
reference<StringReference>()
|
||||
val Instruction.stringReference
|
||||
get() =
|
||||
reference<StringReference>()
|
||||
|
||||
val Instruction.wideLiteral get() =
|
||||
(this as? WideLiteralInstruction)?.wideLiteral
|
||||
val Instruction.wideLiteral
|
||||
get() =
|
||||
(this as? WideLiteralInstruction)?.wideLiteral
|
||||
|
||||
|
||||
private const val CLASS_HEADER = ".class LInlineCompiler;\n.super Ljava/lang/Object;\n"
|
||||
private const val STATIC_HEADER = "$CLASS_HEADER.method public static dummyMethod("
|
||||
private const val HEADER = "$CLASS_HEADER.method public dummyMethod("
|
||||
private val sb by lazy { StringBuilder(512) }
|
||||
|
||||
/**
|
||||
* Compile lines of Smali code to a list of instructions.
|
||||
*
|
||||
* Note: Adding compiled instructions to an existing method with
|
||||
* offset instructions WITHOUT specifying a parent method will not work.
|
||||
* @param templateMethod The method to compile the instructions against.
|
||||
* @returns A list of instructions.
|
||||
*/
|
||||
fun String.toInstructions(templateMethod: MutableMethod? = null): List<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
|
||||
|
||||
import app.revanced.patcher.dex.mutable.MutableMethod
|
||||
import app.revanced.patcher.util.toInstructions
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
||||
|
||||
@@ -21,6 +21,13 @@ typealias PackageName = String
|
||||
typealias VersionName = String
|
||||
typealias Package = Pair<PackageName, Set<VersionName>?>
|
||||
|
||||
|
||||
/**
|
||||
* A common interface for contexts such as [ResourcePatchContext] and [BytecodePatchContext].
|
||||
*/
|
||||
|
||||
sealed interface PatchContext<T> : Supplier<T>
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
import app.revanced.patcher.extensions.toInstructions
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patcher.util.toInstructions
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef
|
||||
@@ -298,7 +298,7 @@ internal object PatcherTest {
|
||||
val matchIndices = indexedMatcher<Instruction>()
|
||||
val method by gettingFirstMethod {
|
||||
implementation {
|
||||
matchIndices(instructions, "match") {
|
||||
matchIndices(instructions) {
|
||||
head { opcode == Opcode.CONST_STRING }
|
||||
add { opcode == Opcode.IPUT_OBJECT }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user