mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-08 16:14:25 +02:00
feat: Add findParentMethod
utility method (#4)
* feat: Add `findParentMethod` utitly method * refactor: add `resolveMethod` to `MethodResolver` added some assertions and some tests Co-authored-by: Lucaskyy <contact@sculas.xyz>
This commit is contained in:
parent
460d62a24c
commit
00c6ab7faf
@ -1,5 +1,7 @@
|
|||||||
package net.revanced.patcher.cache
|
package net.revanced.patcher.cache
|
||||||
|
|
||||||
|
import net.revanced.patcher.resolver.MethodResolver
|
||||||
|
import net.revanced.patcher.signature.Signature
|
||||||
import org.objectweb.asm.tree.ClassNode
|
import org.objectweb.asm.tree.ClassNode
|
||||||
import org.objectweb.asm.tree.MethodNode
|
import org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
@ -7,7 +9,11 @@ data class PatchData(
|
|||||||
val declaringClass: ClassNode,
|
val declaringClass: ClassNode,
|
||||||
val method: MethodNode,
|
val method: MethodNode,
|
||||||
val scanData: PatternScanData
|
val scanData: PatternScanData
|
||||||
)
|
) {
|
||||||
|
fun findParentMethod(signature: Signature): PatchData? {
|
||||||
|
return MethodResolver.resolveMethod(declaringClass, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class PatternScanData(
|
data class PatternScanData(
|
||||||
val startIndex: Int,
|
val startIndex: Int,
|
||||||
|
@ -25,7 +25,7 @@ internal class MethodResolver(private val classList: List<ClassNode>, private va
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logger.debug { "Resolving sig ${signature.name}: ${classNode.name} / ${method.name}" }
|
logger.debug { "Resolving sig ${signature.name}: ${classNode.name} / ${method.name}" }
|
||||||
val (r, sr) = this.cmp(method, signature)
|
val (r, sr) = cmp(method, signature)
|
||||||
if (!r || sr == null) {
|
if (!r || sr == null) {
|
||||||
logger.debug { "Compare result for sig ${signature.name} has failed!" }
|
logger.debug { "Compare result for sig ${signature.name} has failed!" }
|
||||||
continue
|
continue
|
||||||
@ -52,7 +52,22 @@ internal class MethodResolver(private val classList: List<ClassNode>, private va
|
|||||||
return methodMap
|
return methodMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cmp(method: MethodNode, signature: Signature): Pair<Boolean, ScanResult?> {
|
// These functions do not require the constructor values, so they can be static.
|
||||||
|
companion object {
|
||||||
|
fun resolveMethod(classNode: ClassNode, signature: Signature): PatchData? {
|
||||||
|
for (method in classNode.methods) {
|
||||||
|
val (r, sr) = cmp(method, signature, true)
|
||||||
|
if (!r || sr == null) continue
|
||||||
|
return PatchData(
|
||||||
|
classNode,
|
||||||
|
method,
|
||||||
|
PatternScanData(0, 0) // opcode list is always ignored.
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cmp(method: MethodNode, signature: Signature, search: Boolean = false): Pair<Boolean, ScanResult?> {
|
||||||
val returns = Type.getReturnType(method.desc).convertObject()
|
val returns = Type.getReturnType(method.desc).convertObject()
|
||||||
if (signature.returns != returns) {
|
if (signature.returns != returns) {
|
||||||
logger.debug {
|
logger.debug {
|
||||||
@ -66,7 +81,13 @@ internal class MethodResolver(private val classList: List<ClassNode>, private va
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (signature.accessors != method.access) {
|
if (signature.accessors != method.access) {
|
||||||
logger.debug { "Comparing sig ${signature.name}: invalid accessors:\nexpected ${signature.accessors},\ngot ${method.access}" }
|
logger.debug {
|
||||||
|
"""
|
||||||
|
Comparing sig ${signature.name}: invalid accessors:
|
||||||
|
expected ${signature.accessors}},
|
||||||
|
got ${method.access}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
return false to null
|
return false to null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,14 +103,21 @@ internal class MethodResolver(private val classList: List<ClassNode>, private va
|
|||||||
return false to null
|
return false to null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!search) {
|
||||||
|
if (signature.opcodes.isEmpty()) {
|
||||||
|
throw IllegalArgumentException("Opcode list for signature ${signature.name} is empty. This is not allowed for non-search signatures.")
|
||||||
|
}
|
||||||
val result = method.instructions.scanFor(signature.opcodes)
|
val result = method.instructions.scanFor(signature.opcodes)
|
||||||
if (!result.found) {
|
if (!result.found) {
|
||||||
logger.debug { "Comparing sig ${signature.name}: invalid opcode pattern" }
|
logger.debug { "Comparing sig ${signature.name}: invalid opcode pattern" }
|
||||||
return false to null
|
return false to null
|
||||||
}
|
}
|
||||||
|
|
||||||
return true to result
|
return true to result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true to ScanResult(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private operator fun ClassNode.component1(): ClassNode {
|
private operator fun ClassNode.component1(): ClassNode {
|
||||||
|
@ -10,10 +10,14 @@ import org.objectweb.asm.Type
|
|||||||
* If you are unable to guess a method name, doing something like "patch-name-1" is fine too.
|
* If you are unable to guess a method name, doing something like "patch-name-1" is fine too.
|
||||||
* For example: "override-codec-1".
|
* For example: "override-codec-1".
|
||||||
* This method name will be used to find the corresponding patch.
|
* This method name will be used to find the corresponding patch.
|
||||||
|
* Even though this is technically not needed for the `findParentMethod` method,
|
||||||
|
* it is still recommended giving the method a name, so it can be identified easily.
|
||||||
* @param returns The return type/signature of the method.
|
* @param returns The return type/signature of the method.
|
||||||
* @param accessors The accessors of the method.
|
* @param accessors The accessors of the method.
|
||||||
* @param parameters The parameter types of the method.
|
* @param parameters The parameter types of the method.
|
||||||
* @param opcodes The opcode pattern of the method, used to find the method by pattern scanning.
|
* @param opcodes The opcode pattern of the method, used to find the method by pattern scanning.
|
||||||
|
* ***Only if*** **you are using **`findParentMethod`** are you allowed to specify an empty array.**
|
||||||
|
* This parameter will be ignored when using the `findParentMethod` method.
|
||||||
*/
|
*/
|
||||||
@Suppress("ArrayInDataClass")
|
@Suppress("ArrayInDataClass")
|
||||||
data class Signature(
|
data class Signature(
|
||||||
|
@ -9,11 +9,13 @@ import net.revanced.patcher.util.ExtraTypes
|
|||||||
import net.revanced.patcher.util.TestUtil
|
import net.revanced.patcher.util.TestUtil
|
||||||
import net.revanced.patcher.writer.ASMWriter.insertAt
|
import net.revanced.patcher.writer.ASMWriter.insertAt
|
||||||
import net.revanced.patcher.writer.ASMWriter.setAt
|
import net.revanced.patcher.writer.ASMWriter.setAt
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
import org.objectweb.asm.Opcodes.*
|
import org.objectweb.asm.Opcodes.*
|
||||||
import org.objectweb.asm.Type
|
import org.objectweb.asm.Type
|
||||||
import org.objectweb.asm.tree.*
|
import org.objectweb.asm.tree.*
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
internal class PatcherTest {
|
internal class PatcherTest {
|
||||||
companion object {
|
companion object {
|
||||||
@ -145,4 +147,27 @@ internal class PatcherTest {
|
|||||||
// out.close()
|
// out.close()
|
||||||
// testData.close()
|
// testData.close()
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
fun `should raise an exception because opcodes is empty`() {
|
||||||
|
val sigName = "testMethod"
|
||||||
|
val e = assertThrows<IllegalArgumentException>("Should raise an exception because opcodes is empty") {
|
||||||
|
Patcher(
|
||||||
|
PatcherTest::class.java.getResourceAsStream("/test1.jar")!!,
|
||||||
|
arrayOf(
|
||||||
|
Signature(
|
||||||
|
sigName,
|
||||||
|
Type.VOID_TYPE,
|
||||||
|
ACC_PUBLIC or ACC_STATIC,
|
||||||
|
arrayOf(ExtraTypes.ArrayAny),
|
||||||
|
emptyArray() // this is not allowed for non-search signatures!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertEquals(
|
||||||
|
"Opcode list for signature $sigName is empty. This is not allowed for non-search signatures.",
|
||||||
|
e.message
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user