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

View file

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

View file

@ -1,5 +1,7 @@
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.signature.MethodSignature
import app.revanced.patcher.signature.PatternScanMethod
@ -13,14 +15,14 @@ internal class SignatureResolver(
private val classes: List<ClassDef>,
private val methodSignatures: Iterable<MethodSignature>
) {
fun resolve() {
for ((index, classDef) in classes.withIndex()) {
fun resolve(patcherData: PatcherData) {
for (classDef in classes) {
for (signature in methodSignatures) {
for (method in classDef.methods) {
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
// create class proxy, in case a patch needs mutability
val classProxy = ClassProxy(classDef, index)
val classProxy = patcherData.proxy(classDef)
signature.result = SignatureResolverResult(
classProxy,
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.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchMetadata
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.*
import app.revanced.patcher.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
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.util.Preconditions
val packageMetadata = listOf(
PackageMetadata(
"com.example.examplePackage",
listOf("0.0.1", "0.0.2")
)
)
class ExamplePatch : Patch(
PatchMetadata(
"example-patch",
"ReVanced example patch",
"A demonstrative patch to feature the core features of the ReVanced patcher",
listOf("com.example.examplePackage"),
packageMetadata,
"0.0.1"
),
setOf(
@ -48,7 +52,7 @@ class ExamplePatch : Patch(
"main",
),
PatternScanMethod.Fuzzy(1),
listOf("com.example.examplePackage"),
packageMetadata,
"The main method of TestClass",
"1.0.0"
),