mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-11 13:56:16 +00:00
feat: fix method and field access when merging classes
This commit is contained in:
@@ -82,15 +82,16 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
logger.trace("Merging type $type")
|
logger.trace("Merging type $type")
|
||||||
classes.add(classDef)
|
classes.add(classDef)
|
||||||
} else {
|
continue
|
||||||
val (existingClass, existingClassIndex) = result
|
}
|
||||||
|
|
||||||
logger.trace("Type $type exists. Adding missing methods and fields.")
|
val (existingClass, existingClassIndex) = result
|
||||||
|
|
||||||
existingClass.merge(classDef, context, logger).let { mergedClass ->
|
logger.trace("Type $type exists. Adding missing methods and fields.")
|
||||||
if (mergedClass !== existingClass) // referential equality check
|
|
||||||
classes[existingClassIndex] = mergedClass
|
existingClass.merge(classDef, context, logger).let { mergedClass ->
|
||||||
}
|
if (mergedClass !== existingClass) // referential equality check
|
||||||
|
classes[existingClassIndex] = mergedClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import app.revanced.patcher.PatcherContext
|
|||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.logging.Logger
|
import app.revanced.patcher.logging.Logger
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
||||||
|
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
||||||
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
|
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
@@ -30,6 +33,8 @@ internal object ClassMerger {
|
|||||||
* @param logger A logger.
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
fun ClassDef.merge(otherClass: ClassDef, context: PatcherContext, logger: Logger? = null) = this
|
fun ClassDef.merge(otherClass: ClassDef, context: PatcherContext, logger: Logger? = null) = this
|
||||||
|
.fixFieldAccess(otherClass, logger)
|
||||||
|
.fixMethodAccess(otherClass, logger)
|
||||||
.addMissingFields(otherClass, logger)
|
.addMissingFields(otherClass, logger)
|
||||||
.addMissingMethods(otherClass, logger)
|
.addMissingMethods(otherClass, logger)
|
||||||
.publicize(otherClass, context, logger)
|
.publicize(otherClass, context, logger)
|
||||||
@@ -97,6 +102,64 @@ internal object ClassMerger {
|
|||||||
}
|
}
|
||||||
else this
|
else this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publicize fields if they are public in [reference].
|
||||||
|
*
|
||||||
|
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||||
|
* @param logger A logger.
|
||||||
|
*/
|
||||||
|
private fun ClassDef.fixFieldAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
|
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
||||||
|
if (field.name != referenceField.name) return@filterAny false
|
||||||
|
|
||||||
|
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brokenFields.isEmpty()) return this
|
||||||
|
|
||||||
|
logger?.trace("Found ${brokenFields.size} broken fields")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a field public.
|
||||||
|
*/
|
||||||
|
fun MutableField.publicize() {
|
||||||
|
accessFlags = accessFlags.toPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
return asMutableClass().apply {
|
||||||
|
fields.filter { brokenFields.contains(it) }.forEach(MutableField::publicize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publicize methods if they are public in [reference].
|
||||||
|
*
|
||||||
|
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||||
|
* @param logger A logger.
|
||||||
|
*/
|
||||||
|
private fun ClassDef.fixMethodAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
|
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||||
|
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||||
|
|
||||||
|
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brokenMethods.isEmpty()) return this
|
||||||
|
|
||||||
|
logger?.trace("Found ${brokenMethods.size} methods")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a method public.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.publicize() {
|
||||||
|
accessFlags = accessFlags.toPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
return asMutableClass().apply {
|
||||||
|
methods.filter { brokenMethods.contains(it) }.forEach(MutableMethod::publicize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private object Utils {
|
private object Utils {
|
||||||
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
||||||
|
|
||||||
@@ -114,6 +177,18 @@ internal object ClassMerger {
|
|||||||
*/
|
*/
|
||||||
fun Int.toPublic() = this.or(AccessFlags.PUBLIC).and(AccessFlags.PRIVATE.value.inv())
|
fun Int.toPublic() = this.or(AccessFlags.PUBLIC).and(AccessFlags.PRIVATE.value.inv())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter [this] on [needles] matching the given [predicate].
|
||||||
|
*
|
||||||
|
* @param this The hay to filter for [needles].
|
||||||
|
* @param needles The needles to filter [this] with.
|
||||||
|
* @param predicate The filter.
|
||||||
|
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||||
|
*/
|
||||||
|
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
||||||
|
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||||
|
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter [this] on [needles] not matching the given [predicate].
|
* Filter [this] on [needles] not matching the given [predicate].
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user