feat: properly manage ClassProxy & add ProxyBackedClassList

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
oSumAtrIX 2022-04-18 21:37:57 +02:00
parent 7399450139
commit 6cb1fdf617
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
6 changed files with 71 additions and 46 deletions

View File

@ -3,7 +3,6 @@ package app.revanced.patcher
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchMetadata import app.revanced.patcher.patch.PatchMetadata
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.proxy.ClassProxy
import app.revanced.patcher.signature.MethodSignature import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.resolver.SignatureResolver import app.revanced.patcher.signature.resolver.SignatureResolver
import app.revanced.patcher.util.ListBackedSet import app.revanced.patcher.util.ListBackedSet
@ -49,18 +48,18 @@ class Patcher(
for (file in files) { for (file in files) {
val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null) val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null)
for (classDef in dexFile.classes) { for (classDef in dexFile.classes) {
val e = patcherData.classes.findIndexed { it.type == classDef.type } val e = patcherData.classes.internalClasses.findIndexed { it.type == classDef.type }
if (e != null) { if (e != null) {
if (throwOnDuplicates) { if (throwOnDuplicates) {
throw Exception("Class ${classDef.type} has already been added to the patcher.") throw Exception("Class ${classDef.type} has already been added to the patcher.")
} }
val (_, idx) = e val (_, idx) = e
if (allowedOverwrites.contains(classDef.type)) { if (allowedOverwrites.contains(classDef.type)) {
patcherData.classes[idx] = classDef patcherData.classes.internalClasses[idx] = classDef
} }
continue continue
} }
patcherData.classes.add(classDef) patcherData.classes.internalClasses.add(classDef)
} }
} }
} }
@ -70,28 +69,17 @@ class Patcher(
*/ */
fun save(): Map<String, MemoryDataStore> { fun save(): Map<String, MemoryDataStore> {
val newDexFile = object : DexFile { val newDexFile = object : DexFile {
private fun MutableList<ClassDef>.replaceWith(proxy: ClassProxy) {
this[proxy.originalIndex] = proxy.mutatedClass
}
override fun getClasses(): Set<ClassDef> { override fun getClasses(): Set<ClassDef> {
for (proxy in patcherData.classProxies) { val classes = patcherData.classes
val internalClasses = classes.internalClasses
for (proxy in classes.proxies) {
if (!proxy.proxyUsed) continue if (!proxy.proxyUsed) continue
patcherData.classes.replaceWith(proxy) val index = internalClasses.indexOfFirst { it.type == proxy.immutableClass.type }
internalClasses[index] = proxy.mutatedClass
} }
for (patch in patcherData.patches) {
for (signature in patch.signatures) {
val result = signature.result
result ?: continue
val proxy = result.definingClassProxy return ListBackedSet(internalClasses)
if (!proxy.proxyUsed) continue
patcherData.classes.replaceWith(proxy)
}
}
return ListBackedSet(patcherData.classes)
} }
override fun getOpcodes(): Opcodes { override fun getOpcodes(): Opcodes {
@ -129,7 +117,7 @@ class Patcher(
if (signatures.isEmpty()) { if (signatures.isEmpty()) {
throw IllegalStateException("No signatures found to resolve.") throw IllegalStateException("No signatures found to resolve.")
} }
SignatureResolver(patcherData.classes, signatures).resolve() SignatureResolver(patcherData.classes.internalClasses, signatures).resolve(patcherData)
signaturesResolved = true signaturesResolved = true
return signatures return signatures
} }

View File

@ -4,14 +4,15 @@ import app.revanced.patcher.methodWalker.MethodWalker
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.proxy.ClassProxy import app.revanced.patcher.proxy.ClassProxy
import app.revanced.patcher.signature.SignatureResolverResult import app.revanced.patcher.signature.SignatureResolverResult
import app.revanced.patcher.util.ProxyBackedClassList
import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method import org.jf.dexlib2.iface.Method
class PatcherData( class PatcherData(
internal val classes: MutableList<ClassDef>, internalClasses: MutableList<ClassDef>,
) { ) {
internal val classProxies = mutableSetOf<ClassProxy>() val classes = ProxyBackedClassList(internalClasses)
internal val patches = mutableSetOf<Patch>() internal val patches = mutableListOf<Patch>()
/** /**
* Find a class by a given class name * Find a class by a given class name
@ -30,19 +31,14 @@ class PatcherData(
val result = signature.result val result = signature.result
result ?: continue result ?: continue
if (predicate(result.definingClassProxy.immutableClass)) if (predicate(result.definingClassProxy.immutableClass)) return result.definingClassProxy // ...then return that proxy
return result.definingClassProxy // ...then return that proxy
} }
} }
// else search the original class list // else resolve the class to a proxy and return it, if the predicate is matching a class
val (foundClass, index) = classes.findIndexed(predicate) ?: return null return classes.find(predicate)?.let {
// create a class proxy with the index of the class in the classes list proxy(it)
val classProxy = ClassProxy(foundClass, index) }
// add it to the cache and
this.classProxies.add(classProxy)
// return the proxy class
return classProxy
} }
} }
@ -75,3 +71,12 @@ internal inline fun <T> Iterable<T>.findIndexed(predicate: (T) -> Boolean): Pair
} }
return null return null
} }
fun PatcherData.proxy(classProxy: ClassDef): ClassProxy {
var proxy = this.classes.proxies.find { it.immutableClass.type == classProxy.type }
if (proxy == null) {
proxy = ClassProxy(classProxy)
this.classes.proxies.add(proxy)
}
return proxy
}

View File

@ -9,11 +9,9 @@ import org.jf.dexlib2.iface.ClassDef
* A class proxy simply holds a reference to the original class * A class proxy simply holds a reference to the original class
* and allocates a mutable clone for the original class if needed. * and allocates a mutable clone for the original class if needed.
* @param immutableClass The class to proxy * @param immutableClass The class to proxy
* @param originalIndex The original index of the class in the list of classes
*/ */
class ClassProxy( class ClassProxy(
val immutableClass: ClassDef, val immutableClass: ClassDef,
val originalIndex: Int,
) { ) {
internal var proxyUsed = false internal var proxyUsed = false
internal lateinit var mutatedClass: MutableClass internal lateinit var mutatedClass: MutableClass

View File

@ -1,5 +1,7 @@
package app.revanced.patcher.signature.resolver package app.revanced.patcher.signature.resolver
import app.revanced.patcher.PatcherData
import app.revanced.patcher.proxy
import app.revanced.patcher.proxy.ClassProxy import app.revanced.patcher.proxy.ClassProxy
import app.revanced.patcher.signature.MethodSignature import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.PatternScanMethod import app.revanced.patcher.signature.PatternScanMethod
@ -13,14 +15,14 @@ internal class SignatureResolver(
private val classes: List<ClassDef>, private val classes: List<ClassDef>,
private val methodSignatures: Iterable<MethodSignature> private val methodSignatures: Iterable<MethodSignature>
) { ) {
fun resolve() { fun resolve(patcherData: PatcherData) {
for ((index, classDef) in classes.withIndex()) { for (classDef in classes) {
for (signature in methodSignatures) { for (signature in methodSignatures) {
for (method in classDef.methods) { for (method in classDef.methods) {
val patternScanData = compareSignatureToMethod(signature, method) ?: continue val patternScanData = compareSignatureToMethod(signature, method) ?: continue
// create class proxy, in case a patch needs mutability // create class proxy, in case a patch needs mutability
val classProxy = ClassProxy(classDef, index) val classProxy = patcherData.proxy(classDef)
signature.result = SignatureResolverResult( signature.result = SignatureResolverResult(
classProxy, classProxy,
patternScanData, patternScanData,

View File

@ -0,0 +1,28 @@
package app.revanced.patcher.util
import app.revanced.patcher.proxy.ClassProxy
import org.jf.dexlib2.iface.ClassDef
class ProxyBackedClassList(internal val internalClasses: MutableList<ClassDef>) : List<ClassDef> {
internal val proxies = mutableListOf<ClassProxy>()
fun add(classDef: ClassDef) {
internalClasses.add(classDef)
}
fun add(classProxy: ClassProxy) {
proxies.add(classProxy)
}
override val size get() = internalClasses.size
override fun contains(element: ClassDef) = internalClasses.contains(element)
override fun containsAll(elements: Collection<ClassDef>) = internalClasses.containsAll(elements)
override fun get(index: Int) = internalClasses[index]
override fun indexOf(element: ClassDef) = internalClasses.indexOf(element)
override fun isEmpty() = internalClasses.isEmpty()
override fun iterator() = internalClasses.iterator()
override fun lastIndexOf(element: ClassDef) = internalClasses.lastIndexOf(element)
override fun listIterator() = internalClasses.listIterator()
override fun listIterator(index: Int) = internalClasses.listIterator(index)
override fun subList(fromIndex: Int, toIndex: Int) = internalClasses.subList(fromIndex, toIndex)
}

View File

@ -3,10 +3,7 @@ package app.revanced.patcher.usage
import app.revanced.patcher.PatcherData import app.revanced.patcher.PatcherData
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.PatchMetadata
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.signature.MethodMetadata import app.revanced.patcher.signature.MethodMetadata
@ -31,12 +28,19 @@ import org.jf.dexlib2.immutable.reference.ImmutableStringReference
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
import org.jf.dexlib2.util.Preconditions import org.jf.dexlib2.util.Preconditions
val packageMetadata = listOf(
PackageMetadata(
"com.example.examplePackage",
listOf("0.0.1", "0.0.2")
)
)
class ExamplePatch : Patch( class ExamplePatch : Patch(
PatchMetadata( PatchMetadata(
"example-patch", "example-patch",
"ReVanced example patch", "ReVanced example patch",
"A demonstrative patch to feature the core features of the ReVanced patcher", "A demonstrative patch to feature the core features of the ReVanced patcher",
listOf("com.example.examplePackage"), packageMetadata,
"0.0.1" "0.0.1"
), ),
setOf( setOf(
@ -48,7 +52,7 @@ class ExamplePatch : Patch(
"main", "main",
), ),
PatternScanMethod.Fuzzy(1), PatternScanMethod.Fuzzy(1),
listOf("com.example.examplePackage"), packageMetadata,
"The main method of TestClass", "The main method of TestClass",
"1.0.0" "1.0.0"
), ),