Skip to content

Commit cc4f158

Browse files
refactor: Change ProxyList to a map, remove now redundant class lookup map. Encapsulate ProxyList into PatchClasses.
1 parent 142aa71 commit cc4f158

File tree

7 files changed

+167
-110
lines changed

7 files changed

+167
-110
lines changed

api/revanced-patcher.api

+13-37
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,15 @@ public final class app/revanced/patcher/patch/BytecodePatchBuilder : app/revance
311311
}
312312

313313
public final class app/revanced/patcher/patch/BytecodePatchContext : app/revanced/patcher/patch/PatchContext, java/io/Closeable {
314-
public final fun classBy (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
314+
public final fun classBy (Ljava/lang/String;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
315+
public final fun classBy (Lkotlin/jvm/functions/Function1;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
315316
public fun close ()V
316317
public synthetic fun get ()Ljava/lang/Object;
317318
public fun get ()Ljava/util/Set;
318-
public final fun getClasses ()Lapp/revanced/patcher/util/ProxyClassList;
319+
public final fun getClasses ()Lapp/revanced/patcher/util/PatchClasses;
320+
public final fun mutableClassBy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
321+
public final fun mutableClassBy (Ljava/lang/String;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
322+
public final fun mutableClassBy (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
319323
public final fun navigate (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)Lapp/revanced/patcher/util/MethodNavigator;
320324
public final fun proxy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
321325
}
@@ -645,41 +649,13 @@ public final class app/revanced/patcher/util/MethodNavigator {
645649
public static synthetic fun to$default (Lapp/revanced/patcher/util/MethodNavigator;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/util/MethodNavigator;
646650
}
647651

648-
public final class app/revanced/patcher/util/ProxyClassList : java/util/List, kotlin/jvm/internal/markers/KMutableList {
649-
public fun add (ILcom/android/tools/smali/dexlib2/iface/ClassDef;)V
650-
public synthetic fun add (ILjava/lang/Object;)V
651-
public fun add (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
652-
public synthetic fun add (Ljava/lang/Object;)Z
653-
public fun addAll (ILjava/util/Collection;)Z
654-
public fun addAll (Ljava/util/Collection;)Z
655-
public fun clear ()V
656-
public fun contains (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
657-
public final fun contains (Ljava/lang/Object;)Z
658-
public fun containsAll (Ljava/util/Collection;)Z
659-
public fun get (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
660-
public synthetic fun get (I)Ljava/lang/Object;
661-
public fun getSize ()I
662-
public fun indexOf (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)I
663-
public final fun indexOf (Ljava/lang/Object;)I
664-
public fun isEmpty ()Z
665-
public fun iterator ()Ljava/util/Iterator;
666-
public fun lastIndexOf (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)I
667-
public final fun lastIndexOf (Ljava/lang/Object;)I
668-
public fun listIterator ()Ljava/util/ListIterator;
669-
public fun listIterator (I)Ljava/util/ListIterator;
670-
public final fun remove (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
671-
public synthetic fun remove (I)Ljava/lang/Object;
672-
public fun remove (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
673-
public final fun remove (Ljava/lang/Object;)Z
674-
public fun removeAll (Ljava/util/Collection;)Z
675-
public fun removeAt (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
676-
public fun retainAll (Ljava/util/Collection;)Z
677-
public fun set (ILcom/android/tools/smali/dexlib2/iface/ClassDef;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
678-
public synthetic fun set (ILjava/lang/Object;)Ljava/lang/Object;
679-
public final fun size ()I
680-
public fun subList (II)Ljava/util/List;
681-
public fun toArray ()[Ljava/lang/Object;
682-
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
652+
public final class app/revanced/patcher/util/PatchClasses {
653+
public final fun classBy (Ljava/lang/String;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
654+
public final fun classBy (Lkotlin/jvm/functions/Function1;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
655+
public final fun forEach (Lkotlin/jvm/functions/Function1;)V
656+
public final fun mutableClassBy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
657+
public final fun mutableClassBy (Ljava/lang/String;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
658+
public final fun mutableClassBy (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
683659
}
684660

685661
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation : com/android/tools/smali/dexlib2/base/BaseAnnotation {

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

+51-29
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import app.revanced.patcher.PatcherResult
66
import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull
77
import app.revanced.patcher.util.ClassMerger.merge
88
import app.revanced.patcher.util.MethodNavigator
9-
import app.revanced.patcher.util.ProxyClassList
9+
import app.revanced.patcher.util.PatchClasses
1010
import com.android.tools.smali.dexlib2.Opcode
1111
import com.android.tools.smali.dexlib2.Opcodes
1212
import com.android.tools.smali.dexlib2.iface.ClassDef
@@ -43,20 +43,20 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
4343
/**
4444
* The list of classes.
4545
*/
46-
val classes = ProxyClassList(
46+
val classes = PatchClasses(
4747
MultiDexIO.readDexFile(
4848
true,
4949
config.apkFile,
5050
BasicDexFileNamer(),
5151
null,
5252
null,
53-
).also { opcodes = it.opcodes }.classes.toMutableList(),
53+
).also { opcodes = it.opcodes }.classes.associateBy { it.type }.toMutableMap(),
5454
)
5555

5656
/**
5757
* The lookup maps for methods and the class they are a member of from the [classes].
5858
*/
59-
internal val lookupMaps by lazy { LookupMaps(classes) }
59+
internal val lookupMaps by lazy { LookupMaps(classes.pool.values) }
6060

6161
/**
6262
* Merge the extension of [bytecodePatch] into the [BytecodePatchContext].
@@ -67,11 +67,10 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
6767
internal fun mergeExtension(bytecodePatch: BytecodePatch) {
6868
bytecodePatch.extensionInputStream?.get()?.use { extensionStream ->
6969
RawDexIO.readRawDexFile(extensionStream, 0, null).classes.forEach { classDef ->
70-
val existingClass = lookupMaps.classesByType[classDef.type] ?: run {
70+
val existingClass = classes.classBy(classDef.type) ?: run {
7171
logger.fine { "Adding class \"$classDef\"" }
7272

73-
classes += classDef
74-
lookupMaps.classesByType[classDef.type] = classDef
73+
classes.addClass(classDef)
7574

7675
return@forEach
7776
}
@@ -84,29 +83,59 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
8483
return@let
8584
}
8685

87-
classes -= existingClass
88-
classes += mergedClass
86+
classes.addClass(mergedClass)
8987
}
9088
}
9189
} ?: logger.fine("No extension to merge")
9290
}
9391

92+
/**
93+
* Find a class with a predicate.
94+
*
95+
* @param classType The full classname.
96+
* @return An immutable instance of the class type.
97+
* @see mutableClassBy
98+
*/
99+
fun classBy(classType: String) = classes.classBy(classType)
100+
94101
/**
95102
* Find a class with a predicate.
96103
*
97104
* @param predicate A predicate to match the class.
98-
* @return A proxy for the first class that matches the predicate.
105+
* @return An immutable instance of the class type.
106+
* @see mutableClassBy
99107
*/
100-
fun classBy(predicate: (ClassDef) -> Boolean) = classes.proxy(predicate)
108+
fun classBy(predicate: (ClassDef) -> Boolean) = classes.classBy(predicate)
101109

102110
/**
103-
* Proxy the class to allow mutation.
111+
* Find a class with a predicate.
104112
*
105-
* @param classDef The class to proxy.
113+
* @param classType The full classname.
114+
* @return A mutable version of the class type.
115+
*/
116+
fun mutableClassBy(classType: String) = classes.mutableClassBy(classType)
117+
118+
/**
119+
* Find a class with a predicate.
106120
*
107-
* @return A proxy for the class.
121+
* @param classDef An immutable class.
122+
* @return A mutable version of the class definition.
108123
*/
109-
fun proxy(classDef: ClassDef) = classes.proxy(classDef)
124+
fun mutableClassBy(classDef: ClassDef) = classes.mutableClassBy(classDef)
125+
126+
/**
127+
* Find a class with a predicate.
128+
*
129+
* @param predicate A predicate to match the class.
130+
* @return A mutable class that matches the predicate.
131+
*/
132+
fun mutableClassBy(predicate: (ClassDef) -> Boolean) = classes.mutableClassBy(predicate)
133+
134+
/**
135+
* @return The mutable instance of an immutable class.
136+
*/
137+
@Deprecated("Instead use `mutableClassBy(String)`, `mutableClassBy(ClassDef)`, or `mutableClassBy(predicate)`")
138+
fun proxy(classDef: ClassDef) = classes.mutableClassBy(classDef)
110139

111140
/**
112141
* Navigate a method.
@@ -140,7 +169,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
140169
this,
141170
BasicDexFileNamer(),
142171
object : DexFile {
143-
override fun getClasses() = this@BytecodePatchContext.classes.toSet()
172+
override fun getClasses() = this@BytecodePatchContext.classes.pool.values.toSet()
144173

145174
override fun getOpcodes() = this@BytecodePatchContext.opcodes
146175
},
@@ -156,25 +185,22 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
156185
}
157186

158187
/**
159-
* A lookup map for methods and the class they are a member of and classes.
188+
* A lookup map for strings and the methods they are a member of.
160189
*
161190
* @param classes The list of classes to create the lookup maps from.
162191
*/
163-
internal class LookupMaps internal constructor(classes: List<ClassDef>) : Closeable {
192+
internal class LookupMaps internal constructor(classes: Collection<ClassDef>) : Closeable {
164193
/**
165194
* Methods associated by strings referenced in them.
166195
*/
167196
internal val methodsByStrings = MethodClassPairsLookupMap()
168197

169-
// Lookup map for fast checking if a class exists by its type.
170-
val classesByType = mutableMapOf<String, ClassDef>().apply {
171-
classes.forEach { classDef -> put(classDef.type, classDef) }
172-
}
173-
174198
init {
175199
classes.forEach { classDef ->
176200
classDef.methods.forEach { method ->
177-
val methodClassPair: MethodClassPair = method to classDef
201+
val methodClassPair: MethodClassPair by lazy {
202+
method to classDef
203+
}
178204

179205
// Add strings contained in the method as the key.
180206
method.instructionsOrNull?.forEach { instruction ->
@@ -186,22 +212,18 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
186212

187213
methodsByStrings[string] = methodClassPair
188214
}
189-
190-
// In the future, the class type could be added to the lookup map.
191-
// This would require MethodFingerprint to be changed to include the class type.
192215
}
193216
}
194217
}
195218

196219
override fun close() {
197220
methodsByStrings.clear()
198-
classesByType.clear()
199221
}
200222
}
201223

202224
override fun close() {
203225
lookupMaps.close()
204-
classes.clear()
226+
classes.close()
205227
}
206228
}
207229

src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ internal object ClassMerger {
181181
callback(targetClass)
182182

183183
targetClass.superclass ?: return
184-
this.classBy { targetClass.superclass == it.type }?.let {
184+
this.mutableClassBy { targetClass.superclass == it.type }?.let {
185185
traverseClassHierarchy(it, callback)
186186
}
187187
}

src/main/kotlin/app/revanced/patcher/util/MethodNavigator.kt

+2-9
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class MethodNavigator internal constructor(
8080
*
8181
* @return The last navigated method mutably.
8282
*/
83-
fun stop() = classBy(matchesCurrentMethodReferenceDefiningClass)!!.firstMethodBySignature
83+
fun stop() = mutableClassBy(lastNavigatedMethodReference.definingClass).firstMethodBySignature
8484
as MutableMethod
8585

8686
/**
@@ -95,14 +95,7 @@ class MethodNavigator internal constructor(
9595
*
9696
* @return The last navigated method immutably.
9797
*/
98-
fun original(): Method = classes.first(matchesCurrentMethodReferenceDefiningClass).firstMethodBySignature
99-
100-
/**
101-
* Predicate to match the class defining the current method reference.
102-
*/
103-
private val matchesCurrentMethodReferenceDefiningClass = { classDef: ClassDef ->
104-
classDef.type == lastNavigatedMethodReference.definingClass
105-
}
98+
fun original(): Method = classes.classBy(lastNavigatedMethodReference.definingClass)!!.firstMethodBySignature
10699

107100
/**
108101
* Find the first [lastNavigatedMethodReference] in the class.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package app.revanced.patcher.util
2+
3+
import app.revanced.patcher.patch.PatchException
4+
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
5+
import com.android.tools.smali.dexlib2.iface.ClassDef
6+
import kotlin.collections.mutableMapOf
7+
8+
@Deprecated("Instead use PatchClasses")
9+
typealias ProxyClassList = PatchClasses
10+
11+
/**
12+
* A set of all classes for the target app and any extension classes.
13+
*/
14+
class PatchClasses internal constructor(
15+
/**
16+
* Pool of both immutable and mutable classes.
17+
*/
18+
internal val pool: MutableMap<String, ClassDef>
19+
) {
20+
/**
21+
* Mutable classes. All instances are also found in [pool].
22+
*/
23+
private val mutablePool = mutableMapOf<String, MutableClass>()
24+
25+
internal fun addClass(classDef: ClassDef) {
26+
pool[classDef.type] = classDef
27+
}
28+
29+
internal fun close() {
30+
pool.clear()
31+
mutablePool.clear()
32+
}
33+
34+
/**
35+
* Iterate over all classes.
36+
*/
37+
fun forEach(action: (ClassDef) -> Unit) {
38+
pool.values.forEach(action)
39+
}
40+
41+
/**
42+
* Find a class with a predicate.
43+
*
44+
* @param classType The full classname.
45+
* @return An immutable instance of the class type.
46+
* @see mutableClassBy
47+
*/
48+
fun classBy(classType: String) = pool[classType]
49+
50+
/**
51+
* Find a class with a predicate.
52+
*
53+
* @param predicate A predicate to match the class.
54+
* @return An immutable instance of the class type.
55+
*/
56+
fun classBy(predicate: (ClassDef) -> Boolean) = pool.values.find(predicate)
57+
58+
/**
59+
* Find a class with a predicate.
60+
*
61+
* @param classDefType The full classname.
62+
* @return A mutable version of the class type.
63+
*/
64+
fun mutableClassBy(classDefType: String) =
65+
mutablePool[classDefType] ?: MutableClass(
66+
pool.get(classDefType) ?: throw PatchException("Could not find class: $classDefType")
67+
).also {
68+
mutablePool[classDefType] = it
69+
pool[classDefType] = it
70+
}
71+
72+
/**
73+
* Find a class with a predicate.
74+
*
75+
* @param classDef An immutable class.
76+
* @return A mutable version of the class definition.
77+
*/
78+
fun mutableClassBy(classDef: ClassDef) =
79+
mutablePool[classDef.type] ?: MutableClass(classDef).also {
80+
val classType = classDef.type
81+
mutablePool[classType] = it
82+
pool[classType] = it
83+
}
84+
85+
/**
86+
* Find a class with a predicate.
87+
*
88+
* @param predicate A predicate to match the class.
89+
* @return A mutable class that matches the predicate.
90+
*/
91+
fun mutableClassBy(predicate: (ClassDef) -> Boolean) =
92+
mutablePool.values.find { predicate(it) } ?: pool.values.find(predicate)?.let { mutableClassBy(it) }
93+
}

0 commit comments

Comments
 (0)