mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-04-30 05:14:26 +02:00
refactor: Convert extension functions to member functions
BREAKING CHANGE: Some extension functions are now member functions.
This commit is contained in:
parent
6192089b71
commit
e2ca50729d
@ -179,12 +179,12 @@ public abstract class app/revanced/patcher/fingerprint/MethodFingerprint : app/r
|
|||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V
|
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public final fun getResult ()Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
public final fun getResult ()Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
||||||
|
public final fun resolve (Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public final fun resolve (Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
public final fun setResult (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;)V
|
public final fun setResult (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/MethodFingerprint$Companion {
|
public final class app/revanced/patcher/fingerprint/MethodFingerprint$Companion {
|
||||||
public final fun resolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun resolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun resolve (Ljava/lang/Iterable;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/Iterable;)V
|
public final fun resolve (Ljava/lang/Iterable;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/Iterable;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,130 +42,6 @@ abstract class MethodFingerprint(
|
|||||||
*/
|
*/
|
||||||
var result: MethodFingerprintResult? = null
|
var result: MethodFingerprintResult? = null
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* A list of methods and the class they were found in.
|
|
||||||
*/
|
|
||||||
private val methods = mutableListOf<MethodClassPair>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup map for methods keyed to the methods access flags, return type and parameter.
|
|
||||||
*/
|
|
||||||
private val methodSignatureLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup map for methods keyed to the strings contained in the method.
|
|
||||||
*/
|
|
||||||
private val methodStringsLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a string based on the parameter reference types of this method.
|
|
||||||
*/
|
|
||||||
private fun StringBuilder.appendParameters(parameters: Iterable<CharSequence>) {
|
|
||||||
// Maximum parameters to use in the signature key.
|
|
||||||
// Some apps have methods with an incredible number of parameters (over 100 parameters have been seen).
|
|
||||||
// To keep the signature map from becoming needlessly bloated,
|
|
||||||
// group together in the same map entry all methods with the same access/return and 5 or more parameters.
|
|
||||||
// The value of 5 was chosen based on local performance testing and is not set in stone.
|
|
||||||
val maxSignatureParameters = 5
|
|
||||||
// Must append a unique value before the parameters to distinguish this key includes the parameters.
|
|
||||||
// If this is not appended, then methods with no parameters
|
|
||||||
// will collide with different keys that specify access/return but omit the parameters.
|
|
||||||
append("p:")
|
|
||||||
parameters.forEachIndexed { index, parameter ->
|
|
||||||
if (index >= maxSignatureParameters) return
|
|
||||||
append(parameter.first())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes lookup maps for [MethodFingerprint] resolution
|
|
||||||
* using attributes of methods such as the method signature or strings.
|
|
||||||
*
|
|
||||||
* @param context The [BytecodeContext] containing the classes to initialize the lookup maps with.
|
|
||||||
*/
|
|
||||||
internal fun initializeFingerprintResolutionLookupMaps(context: BytecodeContext) {
|
|
||||||
fun MutableMap<String, MutableList<MethodClassPair>>.add(
|
|
||||||
key: String,
|
|
||||||
methodClassPair: MethodClassPair
|
|
||||||
) {
|
|
||||||
var methodClassPairs = this[key]
|
|
||||||
|
|
||||||
methodClassPairs ?: run {
|
|
||||||
methodClassPairs = LinkedList<MethodClassPair>().also { this[key] = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
methodClassPairs!!.add(methodClassPair)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methods.isNotEmpty()) clearFingerprintResolutionLookupMaps()
|
|
||||||
|
|
||||||
context.classes.forEach { classDef ->
|
|
||||||
classDef.methods.forEach { method ->
|
|
||||||
val methodClassPair = method to classDef
|
|
||||||
|
|
||||||
// For fingerprints with no access or return type specified.
|
|
||||||
methods += methodClassPair
|
|
||||||
|
|
||||||
val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first()
|
|
||||||
|
|
||||||
// Add <access><returnType> as the key.
|
|
||||||
methodSignatureLookupMap.add(accessFlagsReturnKey, methodClassPair)
|
|
||||||
|
|
||||||
// Add <access><returnType>[parameters] as the key.
|
|
||||||
methodSignatureLookupMap.add(
|
|
||||||
buildString {
|
|
||||||
append(accessFlagsReturnKey)
|
|
||||||
appendParameters(method.parameterTypes)
|
|
||||||
},
|
|
||||||
methodClassPair
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add strings contained in the method as the key.
|
|
||||||
method.implementation?.instructions?.forEach instructions@{ instruction ->
|
|
||||||
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO)
|
|
||||||
return@instructions
|
|
||||||
|
|
||||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
|
||||||
|
|
||||||
methodStringsLookupMap.add(string, methodClassPair)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the future, the class type could be added to the lookup map.
|
|
||||||
// This would require MethodFingerprint to be changed to include the class type.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the internal lookup maps created in [initializeFingerprintResolutionLookupMaps]
|
|
||||||
*/
|
|
||||||
internal fun clearFingerprintResolutionLookupMaps() {
|
|
||||||
methods.clear()
|
|
||||||
methodSignatureLookupMap.clear()
|
|
||||||
methodStringsLookupMap.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a list of [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
|
||||||
*
|
|
||||||
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
|
|
||||||
* amount of time because they are resolved in sequence.
|
|
||||||
*
|
|
||||||
* For apps with many fingerprints, resolving performance can be improved by:
|
|
||||||
* - Slowest: Specify [opcodes] and nothing else.
|
|
||||||
* - Fast: Specify [accessFlags], [returnType].
|
|
||||||
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
|
||||||
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
|
||||||
*/
|
|
||||||
internal fun Set<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
|
||||||
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
|
||||||
|
|
||||||
forEach { fingerprint ->
|
|
||||||
fingerprint.resolveUsingLookupMap(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
* Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
||||||
*
|
*
|
||||||
@ -178,7 +54,7 @@ abstract class MethodFingerprint(
|
|||||||
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
*/
|
*/
|
||||||
internal fun MethodFingerprint.resolveUsingLookupMap(context: BytecodeContext): Boolean {
|
internal fun resolveUsingLookupMap(context: BytecodeContext): Boolean {
|
||||||
/**
|
/**
|
||||||
* Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
* Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
||||||
*
|
*
|
||||||
@ -238,20 +114,6 @@ abstract class MethodFingerprint(
|
|||||||
return resolveUsingMethodClassPair(methodSignatureLookup())
|
return resolveUsingMethodClassPair(methodSignatureLookup())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
|
|
||||||
*
|
|
||||||
* @param classes The classes on which to resolve the [MethodFingerprint] in.
|
|
||||||
* @param context The [BytecodeContext] to host proxies.
|
|
||||||
* @return True if the resolution was successful, false otherwise.
|
|
||||||
*/
|
|
||||||
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) {
|
|
||||||
for (fingerprint in this) // For each fingerprint...
|
|
||||||
classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint
|
|
||||||
if (fingerprint.resolve(context, classDef))
|
|
||||||
break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a [MethodFingerprint] against a [ClassDef].
|
* Resolve a [MethodFingerprint] against a [ClassDef].
|
||||||
*
|
*
|
||||||
@ -259,7 +121,7 @@ abstract class MethodFingerprint(
|
|||||||
* @param context The [BytecodeContext] to host proxies.
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
* @return True if the resolution was successful, false otherwise.
|
* @return True if the resolution was successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun MethodFingerprint.resolve(context: BytecodeContext, forClass: ClassDef): Boolean {
|
fun resolve(context: BytecodeContext, forClass: ClassDef): Boolean {
|
||||||
for (method in forClass.methods)
|
for (method in forClass.methods)
|
||||||
if (this.resolve(context, method, forClass))
|
if (this.resolve(context, method, forClass))
|
||||||
return true
|
return true
|
||||||
@ -274,7 +136,7 @@ abstract class MethodFingerprint(
|
|||||||
* @param context The [BytecodeContext] to host proxies.
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
* @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise.
|
* @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun MethodFingerprint.resolve(context: BytecodeContext, method: Method, forClass: ClassDef): Boolean {
|
fun resolve(context: BytecodeContext, method: Method, forClass: ClassDef): Boolean {
|
||||||
val methodFingerprint = this
|
val methodFingerprint = this
|
||||||
|
|
||||||
if (methodFingerprint.result != null) return true
|
if (methodFingerprint.result != null) return true
|
||||||
@ -404,6 +266,144 @@ abstract class MethodFingerprint(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* A list of methods and the class they were found in.
|
||||||
|
*/
|
||||||
|
private val methods = mutableListOf<MethodClassPair>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup map for methods keyed to the methods access flags, return type and parameter.
|
||||||
|
*/
|
||||||
|
private val methodSignatureLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup map for methods keyed to the strings contained in the method.
|
||||||
|
*/
|
||||||
|
private val methodStringsLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a string based on the parameter reference types of this method.
|
||||||
|
*/
|
||||||
|
private fun StringBuilder.appendParameters(parameters: Iterable<CharSequence>) {
|
||||||
|
// Maximum parameters to use in the signature key.
|
||||||
|
// Some apps have methods with an incredible number of parameters (over 100 parameters have been seen).
|
||||||
|
// To keep the signature map from becoming needlessly bloated,
|
||||||
|
// group together in the same map entry all methods with the same access/return and 5 or more parameters.
|
||||||
|
// The value of 5 was chosen based on local performance testing and is not set in stone.
|
||||||
|
val maxSignatureParameters = 5
|
||||||
|
// Must append a unique value before the parameters to distinguish this key includes the parameters.
|
||||||
|
// If this is not appended, then methods with no parameters
|
||||||
|
// will collide with different keys that specify access/return but omit the parameters.
|
||||||
|
append("p:")
|
||||||
|
parameters.forEachIndexed { index, parameter ->
|
||||||
|
if (index >= maxSignatureParameters) return
|
||||||
|
append(parameter.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes lookup maps for [MethodFingerprint] resolution
|
||||||
|
* using attributes of methods such as the method signature or strings.
|
||||||
|
*
|
||||||
|
* @param context The [BytecodeContext] containing the classes to initialize the lookup maps with.
|
||||||
|
*/
|
||||||
|
internal fun initializeFingerprintResolutionLookupMaps(context: BytecodeContext) {
|
||||||
|
fun MutableMap<String, MutableList<MethodClassPair>>.add(
|
||||||
|
key: String,
|
||||||
|
methodClassPair: MethodClassPair
|
||||||
|
) {
|
||||||
|
var methodClassPairs = this[key]
|
||||||
|
|
||||||
|
methodClassPairs ?: run {
|
||||||
|
methodClassPairs = LinkedList<MethodClassPair>().also { this[key] = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
methodClassPairs!!.add(methodClassPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.isNotEmpty()) clearFingerprintResolutionLookupMaps()
|
||||||
|
|
||||||
|
context.classes.forEach { classDef ->
|
||||||
|
classDef.methods.forEach { method ->
|
||||||
|
val methodClassPair = method to classDef
|
||||||
|
|
||||||
|
// For fingerprints with no access or return type specified.
|
||||||
|
methods += methodClassPair
|
||||||
|
|
||||||
|
val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first()
|
||||||
|
|
||||||
|
// Add <access><returnType> as the key.
|
||||||
|
methodSignatureLookupMap.add(accessFlagsReturnKey, methodClassPair)
|
||||||
|
|
||||||
|
// Add <access><returnType>[parameters] as the key.
|
||||||
|
methodSignatureLookupMap.add(
|
||||||
|
buildString {
|
||||||
|
append(accessFlagsReturnKey)
|
||||||
|
appendParameters(method.parameterTypes)
|
||||||
|
},
|
||||||
|
methodClassPair
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add strings contained in the method as the key.
|
||||||
|
method.implementation?.instructions?.forEach instructions@{ instruction ->
|
||||||
|
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO)
|
||||||
|
return@instructions
|
||||||
|
|
||||||
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
|
|
||||||
|
methodStringsLookupMap.add(string, methodClassPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the future, the class type could be added to the lookup map.
|
||||||
|
// This would require MethodFingerprint to be changed to include the class type.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the internal lookup maps created in [initializeFingerprintResolutionLookupMaps]
|
||||||
|
*/
|
||||||
|
internal fun clearFingerprintResolutionLookupMaps() {
|
||||||
|
methods.clear()
|
||||||
|
methodSignatureLookupMap.clear()
|
||||||
|
methodStringsLookupMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a list of [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
||||||
|
*
|
||||||
|
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
|
||||||
|
* amount of time because they are resolved in sequence.
|
||||||
|
*
|
||||||
|
* For apps with many fingerprints, resolving performance can be improved by:
|
||||||
|
* - Slowest: Specify [opcodes] and nothing else.
|
||||||
|
* - Fast: Specify [accessFlags], [returnType].
|
||||||
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
|
*/
|
||||||
|
internal fun Set<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
||||||
|
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
||||||
|
|
||||||
|
forEach { fingerprint ->
|
||||||
|
fingerprint.resolveUsingLookupMap(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
|
||||||
|
*
|
||||||
|
* @param classes The classes on which to resolve the [MethodFingerprint] in.
|
||||||
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
|
* @return True if the resolution was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) {
|
||||||
|
for (fingerprint in this) // For each fingerprint...
|
||||||
|
classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint
|
||||||
|
if (fingerprint.resolve(context, classDef))
|
||||||
|
break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint.
|
||||||
|
}
|
||||||
|
|
||||||
private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings(
|
private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings(
|
||||||
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
||||||
) = buildList {
|
) = buildList {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user