Skip to content

Commit 4d38837

Browse files
feat: Add 'classFingerprint' (parent fingerprint)
1 parent 9779e50 commit 4d38837

File tree

4 files changed

+45
-17
lines changed

4 files changed

+45
-17
lines changed

api/revanced-patcher.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public final class app/revanced/patcher/Fingerprint {
3737
public final fun match (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
3838
public final fun matchOrNull ()Lapp/revanced/patcher/Match;
3939
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
40+
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
4041
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
41-
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
4242
public final fun patchException ()Lapp/revanced/patcher/patch/PatchException;
4343
public fun toString ()Ljava/lang/String;
4444
}
@@ -48,6 +48,7 @@ public final class app/revanced/patcher/FingerprintBuilder {
4848
public final fun accessFlags (I)V
4949
public final fun accessFlags ([Lcom/android/tools/smali/dexlib2/AccessFlags;)V
5050
public final fun build ()Lapp/revanced/patcher/Fingerprint;
51+
public final fun classFingerprint (Lapp/revanced/patcher/Fingerprint;)V
5152
public final fun custom (Lkotlin/jvm/functions/Function2;)V
5253
public final fun getName ()Ljava/lang/String;
5354
public final fun instructions ([Lapp/revanced/patcher/InstructionFilter;)V

src/main/kotlin/app/revanced/patcher/Fingerprint.kt

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ internal fun parametersStartsWith(
5858
* ```
5959
*
6060
* @param name Human readable name used for [toString].
61+
* @param classFingerprint Fingerprint that matches any method in the class this fingerprint matches to.
6162
* @param accessFlags The exact access flags using values of [AccessFlags].
6263
* @param returnType The return type. Compared using [String.startsWith].
6364
* @param parameters The parameters. Partial matches allowed and follow the same rules as [returnType].
@@ -67,6 +68,7 @@ internal fun parametersStartsWith(
6768
*/
6869
class Fingerprint internal constructor(
6970
internal val name: String,
71+
internal val classFingerprint: Fingerprint? = null,
7072
internal val accessFlags: Int?,
7173
internal val returnType: String?,
7274
internal val parameters: List<String>?,
@@ -86,6 +88,11 @@ class Fingerprint internal constructor(
8688

8789
val start = if (LOG_RESOLVING_SPEED) System.currentTimeMillis() else 0
8890

91+
if (classFingerprint != null) {
92+
_matchOrNull = matchOrNull(classFingerprint.match().originalClassDef)
93+
return _matchOrNull
94+
}
95+
8996
strings?.mapNotNull {
9097
BytecodePatchContext.lookupMaps.methodsByStrings[it]
9198
}?.minByOrNull { it.size }?.let { methodClasses ->
@@ -129,7 +136,7 @@ class Fingerprint internal constructor(
129136
if (_matchOrNull != null) return _matchOrNull
130137

131138
for (method in classDef.methods) {
132-
val match = matchOrNull(method, classDef)
139+
val match = matchOrNull(classDef, method)
133140
if (match != null) {
134141
_matchOrNull = match
135142
return match
@@ -151,7 +158,10 @@ class Fingerprint internal constructor(
151158
): Match? {
152159
if (_matchOrNull != null) return _matchOrNull
153160

154-
return matchOrNull(method, BytecodePatchContext.classBy { method.definingClass == it.type }!!.immutableClass)
161+
return matchOrNull(
162+
BytecodePatchContext.classBy { method.definingClass == it.type }!!.immutableClass,
163+
method
164+
)
155165
}
156166

157167
/**
@@ -162,9 +172,14 @@ class Fingerprint internal constructor(
162172
* @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise.
163173
*/
164174
fun matchOrNull(
165-
method: Method,
166175
classDef: ClassDef,
176+
method: Method,
167177
): Match? {
178+
if (classFingerprint != null && classFingerprint.match().classDef != classDef) {
179+
throw PatchException("Fingerprint $this declares a class fingerprint," +
180+
"but tried to match using a different class: $classDef ")
181+
}
182+
168183
if (_matchOrNull != null) return _matchOrNull
169184

170185
if (returnType != null && !method.returnType.startsWith(returnType)) {
@@ -328,7 +343,7 @@ class Fingerprint internal constructor(
328343
fun match(
329344
method: Method,
330345
classDef: ClassDef,
331-
) = matchOrNull(method, classDef) ?: throw patchException()
346+
) = matchOrNull(classDef, method) ?: throw patchException()
332347

333348
/**
334349
* The class the matching method is a member of, or null if this fingerprint did not match.
@@ -533,6 +548,7 @@ class Match internal constructor(
533548
* A builder for [Fingerprint].
534549
*
535550
* @property name Name of the fingerprint, and usually identical to the variable name.
551+
* @property classFingerprint Fingerprint used to find the class this fingerprint resolves to.
536552
* @property accessFlags The exact access flags using values of [AccessFlags].
537553
* @property returnType The return type compared using [String.startsWith].
538554
* @property parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType].
@@ -543,13 +559,23 @@ class Match internal constructor(
543559
* @constructor Create a new [FingerprintBuilder].
544560
*/
545561
class FingerprintBuilder(val name: String) {
562+
private var classFingerprint: Fingerprint? = null
546563
private var accessFlags: Int? = null
547564
private var returnType: String? = null
548565
private var parameters: List<String>? = null
549566
private var instructionFilters: List<InstructionFilter>? = null
550567
private var strings: List<String>? = null
551568
private var customBlock: ((method: Method, classDef: ClassDef) -> Boolean)? = null
552569

570+
/**
571+
* Sets the class (parent) fingerprint.
572+
*
573+
* @param classFingerprint Fingerprint that finds any other methods in the target class.
574+
*/
575+
fun classFingerprint(classFingerprint: Fingerprint) {
576+
this.classFingerprint = classFingerprint
577+
}
578+
553579
/**
554580
* Set the access flags.
555581
*
@@ -660,6 +686,7 @@ class FingerprintBuilder(val name: String) {
660686

661687
fun build() = Fingerprint(
662688
name,
689+
classFingerprint,
663690
accessFlags,
664691
returnType,
665692
parameters,

src/main/kotlin/app/revanced/patcher/InstructionFilter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class MethodFilter(
253253
// Would be nice if this also checked all super classes,
254254
// but doing so requires iteratively checking all superclasses
255255
// up to the root Object class since class defs are mere Strings.
256-
if (definingClass != "this" || referenceClass != method.definingClass) {
256+
if (!(definingClass == "this" && referenceClass == method.definingClass)) {
257257
return false
258258
} // else, the method call is for 'this' class.
259259
}
@@ -343,7 +343,7 @@ class FieldFilter(
343343
val definingClass = definingClass()
344344

345345
if (!referenceClass.endsWith(definingClass)) {
346-
if (definingClass != "this" || referenceClass != method.definingClass) {
346+
if (!(definingClass === "this" && referenceClass == method.definingClass)) {
347347
return false
348348
} // else, the method call is for 'this' class.
349349
}

src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
192192
init {
193193
classes.forEach { classDef ->
194194
classDef.methods.forEach { method ->
195-
val methodClassPair: MethodClassPair = method to classDef
195+
val classMethodPair: ClassMethodPair = classDef to method
196196

197197
// Add strings contained in the method as the key.
198198
method.instructionsOrNull?.forEach { instruction ->
@@ -203,7 +203,7 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
203203
}
204204

205205
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
206-
methodsByStrings[string] = methodClassPair
206+
methodsByStrings[string] = classMethodPair
207207
}
208208

209209
// In the future, the class type could be added to the lookup map.
@@ -227,22 +227,22 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
227227
/**
228228
* A pair of a [Method] and the [ClassDef] it is a member of.
229229
*/
230-
internal typealias MethodClassPair = Pair<Method, ClassDef>
230+
internal typealias ClassMethodPair = Pair<ClassDef, Method>
231231

232232
/**
233-
* A list of [MethodClassPair]s.
233+
* A list of [ClassMethodPair]s.
234234
*/
235-
internal typealias MethodClassPairs = LinkedList<MethodClassPair>
235+
internal typealias MethodClassPairs = LinkedList<ClassMethodPair>
236236

237237
/**
238238
* A lookup map for [MethodClassPairs]s.
239-
* The key is a string and the value is a list of [MethodClassPair]s.
239+
* The key is a string and the value is a list of [ClassMethodPair]s.
240240
*/
241241
internal class MethodClassPairsLookupMap : MutableMap<String, MethodClassPairs> by mutableMapOf() {
242242
/**
243-
* Add a [MethodClassPair] associated by any key.
244-
* If the key does not exist, a new list is created and the [MethodClassPair] is added to it.
243+
* Add a [ClassMethodPair] associated by any key.
244+
* If the key does not exist, a new list is created and the [ClassMethodPair] is added to it.
245245
*/
246-
internal operator fun set(key: String, methodClassPair: MethodClassPair) =
247-
apply { getOrPut(key) { MethodClassPairs() }.add(methodClassPair) }
246+
internal operator fun set(key: String, classMethodPair: ClassMethodPair) =
247+
apply { getOrPut(key) { MethodClassPairs() }.add(classMethodPair) }
248248
}

0 commit comments

Comments
 (0)