Skip to content

Commit

Permalink
feat: properly manage ClassProxy & add ProxyBackedClassList
Browse files Browse the repository at this point in the history
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
  • Loading branch information
oSumAtrIX committed Jun 5, 2022
1 parent 7399450 commit 6cb1fdf
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 46 deletions.
32 changes: 10 additions & 22 deletions src/main/kotlin/app/revanced/patcher/Patcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
31 changes: 18 additions & 13 deletions src/main/kotlin/app/revanced/patcher/PatcherData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
}

Expand Down Expand Up @@ -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
}
2 changes: 0 additions & 2 deletions src/main/kotlin/app/revanced/patcher/proxy/ClassProxy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand Down
28 changes: 28 additions & 0 deletions src/main/kotlin/app/revanced/patcher/util/ProxyBackedClassList.kt
Original file line number Diff line number Diff line change
@@ -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)
}
16 changes: 10 additions & 6 deletions src/test/kotlin/app/revanced/patcher/usage/ExamplePatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -48,7 +52,7 @@ class ExamplePatch : Patch(
"main",
),
PatternScanMethod.Fuzzy(1),
listOf("com.example.examplePackage"),
packageMetadata,
"The main method of TestClass",
"1.0.0"
),
Expand Down

0 comments on commit 6cb1fdf

Please sign in to comment.