mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 01:02:22 +01:00
test: improve PatcherTest
This commit is contained in:
parent
0e72a6e85f
commit
ec8115f3ac
3 changed files with 91 additions and 17 deletions
|
@ -7,4 +7,14 @@ object ASMWriter {
|
|||
fun InsnList.setAt(index: Int, node: AbstractInsnNode) {
|
||||
this[this.get(index)] = node
|
||||
}
|
||||
fun InsnList.insertAt(index: Int, vararg nodes: AbstractInsnNode) {
|
||||
this.insert(this.get(index), nodes.toInsnList())
|
||||
}
|
||||
|
||||
// TODO(Sculas): Should this be public?
|
||||
private fun Array<out AbstractInsnNode>.toInsnList(): InsnList {
|
||||
val list = InsnList()
|
||||
this.forEach { list.add(it) }
|
||||
return list
|
||||
}
|
||||
}
|
|
@ -4,10 +4,13 @@ import net.revanced.patcher.patch.Patch
|
|||
import net.revanced.patcher.patch.PatchResultSuccess
|
||||
import net.revanced.patcher.signature.Signature
|
||||
import net.revanced.patcher.util.ExtraTypes
|
||||
import net.revanced.patcher.util.TestUtil
|
||||
import net.revanced.patcher.writer.ASMWriter.insertAt
|
||||
import net.revanced.patcher.writer.ASMWriter.setAt
|
||||
import org.objectweb.asm.Opcodes.*
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.tree.*
|
||||
import java.io.PrintStream
|
||||
import kotlin.test.Test
|
||||
|
||||
internal class PatcherTest {
|
||||
|
@ -46,20 +49,51 @@ internal class PatcherTest {
|
|||
val mainMethod = patcher.cache.methods["mainMethod"]
|
||||
// Get the instruction list
|
||||
val instructions = mainMethod.method.instructions!!
|
||||
// Let's modify it, so it prints "Hello, ReVanced!"
|
||||
// Get the start index of our opcode pattern
|
||||
// This will be the index of the LDC instruction
|
||||
|
||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
||||
// Get the start index of our opcode pattern.
|
||||
// This will be the index of the LDC instruction.
|
||||
val startIndex = mainMethod.scanData.startIndex
|
||||
// Create a new Ldc node and replace the LDC instruction
|
||||
val stringNode = LdcInsnNode("Hello, ReVanced!");
|
||||
TestUtil.assertNodeEqual(LdcInsnNode("Hello, world!"), instructions[startIndex]!!)
|
||||
// Create a new LDC node and replace the LDC instruction.
|
||||
val stringNode = LdcInsnNode("Hello, ReVanced! Editing bytecode.")
|
||||
instructions.setAt(startIndex, stringNode)
|
||||
// Now lets print our string to the console output
|
||||
// First create a list of instructions
|
||||
val printCode = InsnList();
|
||||
printCode.add(LdcInsnNode("Hello, ReVanced!"))
|
||||
printCode.add(MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"))
|
||||
// Add the list after the second instruction by our pattern
|
||||
instructions.insert(instructions[startIndex + 1], printCode)
|
||||
|
||||
// Now lets print our string twice!
|
||||
// Insert our instructions after the second instruction by our pattern.
|
||||
// This will place our instructions after the original INVOKEVIRTUAL call.
|
||||
// You could also copy the instructions from the list and then modify the LDC instruction again,
|
||||
// but this is to show a more advanced example of writing bytecode using the patcher and ASM.
|
||||
instructions.insertAt(
|
||||
startIndex + 1,
|
||||
FieldInsnNode(
|
||||
GETSTATIC,
|
||||
Type.getInternalName(System::class.java), // "java/io/System"
|
||||
"out",
|
||||
Type.getInternalName(PrintStream::class.java) // "java.io.PrintStream"
|
||||
),
|
||||
LdcInsnNode("Hello, ReVanced! Adding bytecode."),
|
||||
MethodInsnNode(
|
||||
INVOKEVIRTUAL,
|
||||
Type.getInternalName(PrintStream::class.java), // "java/io/PrintStream"
|
||||
"println",
|
||||
Type.getMethodDescriptor(
|
||||
Type.VOID_TYPE,
|
||||
Type.getType(String::class.java)
|
||||
) // "(Ljava/lang/String;)V"
|
||||
)
|
||||
)
|
||||
|
||||
// Our code now looks like this:
|
||||
// public static main(java.lang.String[] arg0) { // Method signature: ([Ljava/lang/String;)V
|
||||
// getstatic java/lang/System.out:java.io.PrintStream
|
||||
// ldc "Hello, ReVanced! Editing bytecode." (java.lang.String) // We overwrote this instruction.
|
||||
// invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
|
||||
// getstatic java/lang/System.out:java.io.PrintStream // This instruction and the 2 instructions below are written manually.
|
||||
// ldc "Hello, ReVanced! Adding bytecode." (java.lang.String)
|
||||
// invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
|
||||
// return
|
||||
// }
|
||||
|
||||
// Finally, tell the patcher that this patch was a success.
|
||||
// You can also return PatchResultError with a message.
|
||||
|
|
|
@ -1,14 +1,44 @@
|
|||
package net.revanced.patcher.util
|
||||
|
||||
import org.objectweb.asm.tree.AbstractInsnNode
|
||||
import kotlin.test.assertTrue
|
||||
import org.objectweb.asm.tree.FieldInsnNode
|
||||
import org.objectweb.asm.tree.LdcInsnNode
|
||||
import kotlin.test.fail
|
||||
|
||||
object TestUtil {
|
||||
fun <T: AbstractInsnNode> assertNodeEqual(a: T, b: T) {
|
||||
|
||||
fun <T: AbstractInsnNode> assertNodeEqual(expected: T, actual: T) {
|
||||
val a = expected.nodeString()
|
||||
val b = actual.nodeString()
|
||||
if (a != b) {
|
||||
fail("expected: $a,\nactual: $b\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.nodeString(): String {
|
||||
|
||||
val sb = NodeStringBuilder()
|
||||
when (this) {
|
||||
// TODO: Add more types
|
||||
is LdcInsnNode -> sb
|
||||
.addType("cst", cst)
|
||||
is FieldInsnNode -> sb
|
||||
.addType("owner", owner)
|
||||
.addType("name", name)
|
||||
.addType("desc", desc)
|
||||
}
|
||||
return "(${this::class.simpleName}): (type = $type, opcode = $opcode, $sb)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NodeStringBuilder {
|
||||
private val sb = StringBuilder()
|
||||
|
||||
fun addType(name: String, value: Any): NodeStringBuilder {
|
||||
sb.append("$name = \"$value\", ")
|
||||
return this
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val s = sb.toString()
|
||||
return s.substring(0 until s.length - 2) // remove the last ", "
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue