feat: simplify adding instructions

This commit is contained in:
oSumAtrIX 2022-06-20 21:20:51 +02:00
parent ad6c5c8273
commit e47b67d7ec
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
3 changed files with 51 additions and 22 deletions

View file

@ -1,6 +1,9 @@
package app.revanced.patcher.extensions package app.revanced.patcher.extensions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.builder.BuilderInstruction import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.builder.MutableMethodImplementation import org.jf.dexlib2.builder.MutableMethodImplementation
@ -62,6 +65,22 @@ internal fun Method.clone(
) )
} }
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param instruction The smali instruction to add.
*/
fun MutableMethod.addInstruction(index: Int, instruction: String) =
this.implementation!!.addInstruction(index, instruction.toInstruction(this))
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param instructions The smali instructions to add.
*/
fun MutableMethod.addInstructions(index: Int, instructions: String) =
this.implementation!!.addInstructions(index, instructions.toInstructions(this))
/** /**
* Clones the method. * Clones the method.
* @param registerCount This parameter allows you to change the register count of the method. * @param registerCount This parameter allows you to change the register count of the method.

View file

@ -3,8 +3,10 @@ package app.revanced.patcher.util.smali
import org.antlr.runtime.CommonTokenStream import org.antlr.runtime.CommonTokenStream
import org.antlr.runtime.TokenSource import org.antlr.runtime.TokenSource
import org.antlr.runtime.tree.CommonTreeNodeStream import org.antlr.runtime.tree.CommonTreeNodeStream
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcodes import org.jf.dexlib2.Opcodes
import org.jf.dexlib2.builder.BuilderInstruction import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.writer.builder.DexBuilder import org.jf.dexlib2.writer.builder.DexBuilder
import org.jf.smali.LexerErrorInterface import org.jf.smali.LexerErrorInterface
import org.jf.smali.smaliFlexLexer import org.jf.smali.smaliFlexLexer
@ -12,17 +14,17 @@ import org.jf.smali.smaliParser
import org.jf.smali.smaliTreeWalker import org.jf.smali.smaliTreeWalker
import java.io.InputStreamReader import java.io.InputStreamReader
private const val METHOD_TEMPLATE = """
.class LInlineCompiler;
.super Ljava/lang/Object;
.method %s dummyMethod(%s)V
.registers %d
%s
.end method
"""
class InlineSmaliCompiler { class InlineSmaliCompiler {
companion object { companion object {
private const val METHOD_TEMPLATE = """
.class LInlineCompiler;
.super Ljava/lang/Object;
.method %s dummyMethod(%s)V
.registers %d
%s
.end method
"""
/** /**
* Compiles a string of Smali code to a list of instructions. * Compiles a string of Smali code to a list of instructions.
* p0, p1 etc. will only work correctly if the parameters and registers are passed. * p0, p1 etc. will only work correctly if the parameters and registers are passed.
@ -33,12 +35,10 @@ class InlineSmaliCompiler {
* FIXME: Fix the above issue. When this is fixed, add the proper conversions in [InstructionConverter]. * FIXME: Fix the above issue. When this is fixed, add the proper conversions in [InstructionConverter].
*/ */
fun compileMethodInstructions( fun compileMethodInstructions(
instructions: String, instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean
parameters: String,
registers: Int,
forStaticMethod: Boolean
): List<BuilderInstruction> { ): List<BuilderInstruction> {
val input = METHOD_TEMPLATE.format(if (forStaticMethod) "static" else "", parameters, registers, instructions) val input =
METHOD_TEMPLATE.format(if (forStaticMethod) "static" else "", parameters, registers, instructions)
val reader = InputStreamReader(input.byteInputStream()) val reader = InputStreamReader(input.byteInputStream())
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15) val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
val tokens = CommonTokenStream(lexer as TokenSource) val tokens = CommonTokenStream(lexer as TokenSource)
@ -59,8 +59,19 @@ class InlineSmaliCompiler {
} }
} }
fun String.toInstructions(parametersCount: Int = 0, registers: Int = 1, forStaticMethod: Boolean = true) = /**
InlineSmaliCompiler.compileMethodInstructions(this, "I".repeat(parametersCount), registers, forStaticMethod) * Compile lines of Smali code to a list of instructions.
* @param templateMethod The method to compile the instructions against.
* @returns A list of instructions.
*/
fun String.toInstructions(templateMethod: Method? = null) = InlineSmaliCompiler.compileMethodInstructions(this,
templateMethod?.parameters?.joinToString("") { it } ?: "",
templateMethod?.implementation?.registerCount ?: 0,
(templateMethod?.accessFlags ?: 0) and AccessFlags.STATIC.value != 0)
fun String.toInstruction(parametersCount: Int = 0, registers: Int = 1, forStaticMethod: Boolean = true) = /**
this.toInstructions(parametersCount, registers, forStaticMethod).first() * Compile a line of Smali code to an instruction.
* @param templateMethod The method to compile the instructions against.
* @return The instruction.
*/
fun String.toInstruction(templateMethod: Method? = null) = this.toInstructions(templateMethod).first()

View file

@ -15,7 +15,6 @@ import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibili
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstruction import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Format import org.jf.dexlib2.Format
@ -47,7 +46,7 @@ class ExampleBytecodePatch : BytecodePatch(
// You can treat it as a constructor // You can treat it as a constructor
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData): PatchResult {
// Get the resolved method for the signature from the resolver cache // Get the resolved method for the signature from the resolver cache
val result = signatures.first().result!! val result = ExampleSignature.result!!
// Get the implementation for the resolved method // Get the implementation for the resolved method
val implementation = result.method.implementation!! val implementation = result.method.implementation!!
@ -126,8 +125,8 @@ class ExampleBytecodePatch : BytecodePatch(
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String; invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
move-result-object v1 move-result-object v1
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
""".trimIndent().toInstructions() """
implementation.addInstructions(startIndex + 2, instructions) result.method.addInstructions(startIndex + 2, instructions)
// Finally, tell the patcher that this patch was a success. // Finally, tell the patcher that this patch was a success.
// You can also return PatchResultError with a message. // You can also return PatchResultError with a message.