diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt index 5f081c35..46de5350 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt @@ -1,6 +1,9 @@ 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.smali.toInstruction +import app.revanced.patcher.util.smali.toInstructions import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.builder.BuilderInstruction 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. * @param registerCount This parameter allows you to change the register count of the method. diff --git a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt index c8c62113..fae3665f 100644 --- a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt +++ b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt @@ -3,8 +3,10 @@ package app.revanced.patcher.util.smali import org.antlr.runtime.CommonTokenStream import org.antlr.runtime.TokenSource import org.antlr.runtime.tree.CommonTreeNodeStream +import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.Opcodes import org.jf.dexlib2.builder.BuilderInstruction +import org.jf.dexlib2.iface.Method import org.jf.dexlib2.writer.builder.DexBuilder import org.jf.smali.LexerErrorInterface import org.jf.smali.smaliFlexLexer @@ -12,17 +14,17 @@ import org.jf.smali.smaliParser import org.jf.smali.smaliTreeWalker 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 { 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. * 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]. */ fun compileMethodInstructions( - instructions: String, - parameters: String, - registers: Int, - forStaticMethod: Boolean + instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean ): List { - 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 lexer: LexerErrorInterface = smaliFlexLexer(reader, 15) 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() diff --git a/src/test/kotlin/app/revanced/patcher/usage/bytecode/patch/ExampleBytecodePatch.kt b/src/test/kotlin/app/revanced/patcher/usage/bytecode/patch/ExampleBytecodePatch.kt index 2d58b9bb..f8bd8f25 100644 --- a/src/test/kotlin/app/revanced/patcher/usage/bytecode/patch/ExampleBytecodePatch.kt +++ b/src/test/kotlin/app/revanced/patcher/usage/bytecode/patch/ExampleBytecodePatch.kt @@ -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.MutableMethod.Companion.toMutable import app.revanced.patcher.util.smali.toInstruction -import app.revanced.patcher.util.smali.toInstructions import com.google.common.collect.ImmutableList import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.Format @@ -47,7 +46,7 @@ class ExampleBytecodePatch : BytecodePatch( // You can treat it as a constructor override fun execute(data: BytecodeData): PatchResult { // 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 val implementation = result.method.implementation!! @@ -126,8 +125,8 @@ class ExampleBytecodePatch : BytecodePatch( invoke-static { }, LTestClass;->returnHello()Ljava/lang/String; move-result-object v1 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. // You can also return PatchResultError with a message.