Skip to content

Commit

Permalink
feat: simplify adding instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
oSumAtrIX committed Jun 20, 2022
1 parent ad6c5c8 commit e47b67d
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 22 deletions.
19 changes: 19 additions & 0 deletions src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@ 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
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.
Expand All @@ -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<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 lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
val tokens = CommonTokenStream(lexer as TokenSource)
Expand All @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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!!
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit e47b67d

Please sign in to comment.