From 6cb1fdf6171e1ab75b7ee28163965eacc00cc5a0 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 18 Apr 2022 21:37:57 +0200 Subject: [PATCH] feat: properly manage `ClassProxy` & add `ProxyBackedClassList` Signed-off-by: oSumAtrIX --- .../kotlin/app/revanced/patcher/Patcher.kt | 32 ++++++------------- .../app/revanced/patcher/PatcherData.kt | 31 ++++++++++-------- .../app/revanced/patcher/proxy/ClassProxy.kt | 2 -- .../signature/resolver/SignatureResolver.kt | 8 +++-- .../patcher/util/ProxyBackedClassList.kt | 28 ++++++++++++++++ .../revanced/patcher/usage/ExamplePatch.kt | 16 ++++++---- 6 files changed, 71 insertions(+), 46 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 628a291..3f3160b 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -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 { val newDexFile = object : DexFile { - private fun MutableList.replaceWith(proxy: ClassProxy) { - this[proxy.originalIndex] = proxy.mutatedClass - } - override fun getClasses(): Set { - 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 } diff --git a/src/main/kotlin/app/revanced/patcher/PatcherData.kt b/src/main/kotlin/app/revanced/patcher/PatcherData.kt index 94555bc..97701f5 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherData.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherData.kt @@ -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, + internalClasses: MutableList, ) { - internal val classProxies = mutableSetOf() - internal val patches = mutableSetOf() + val classes = ProxyBackedClassList(internalClasses) + internal val patches = mutableListOf() /** * 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 Iterable.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 +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/proxy/ClassProxy.kt b/src/main/kotlin/app/revanced/patcher/proxy/ClassProxy.kt index 4b27eb5..9511985 100644 --- a/src/main/kotlin/app/revanced/patcher/proxy/ClassProxy.kt +++ b/src/main/kotlin/app/revanced/patcher/proxy/ClassProxy.kt @@ -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 diff --git a/src/main/kotlin/app/revanced/patcher/signature/resolver/SignatureResolver.kt b/src/main/kotlin/app/revanced/patcher/signature/resolver/SignatureResolver.kt index 6a6bf88..a1f6de4 100644 --- a/src/main/kotlin/app/revanced/patcher/signature/resolver/SignatureResolver.kt +++ b/src/main/kotlin/app/revanced/patcher/signature/resolver/SignatureResolver.kt @@ -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, private val methodSignatures: Iterable ) { - 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, diff --git a/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt b/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt new file mode 100644 index 0000000..6d68388 --- /dev/null +++ b/src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt @@ -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) : List { + internal val proxies = mutableListOf() + + 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) = 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) +} \ No newline at end of file diff --git a/src/test/kotlin/app/revanced/patcher/usage/ExamplePatch.kt b/src/test/kotlin/app/revanced/patcher/usage/ExamplePatch.kt index b96be4f..e0c506c 100644 --- a/src/test/kotlin/app/revanced/patcher/usage/ExamplePatch.kt +++ b/src/test/kotlin/app/revanced/patcher/usage/ExamplePatch.kt @@ -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" ),