fix: Merge extension only when patch executes (#315)

This commit is contained in:
oSumAtrIX 2024-10-27 16:00:30 +01:00 committed by GitHub
parent ab624f04f6
commit aa472eb985
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 36 additions and 37 deletions

View File

@ -91,19 +91,15 @@ class Patcher(private val config: PatcherConfig) : Closeable {
}.also { executedPatches[this] = it } }.also { executedPatches[this] = it }
} }
// Prevent from decoding the app manifest twice if it is not needed. // Prevent decoding the app manifest twice if it is not needed.
if (config.resourceMode != ResourcePatchContext.ResourceMode.NONE) { if (config.resourceMode != ResourcePatchContext.ResourceMode.NONE) {
context.resourceContext.decodeResources(config.resourceMode) context.resourceContext.decodeResources(config.resourceMode)
} }
logger.info("Merging extensions") logger.info("Initializing lookup maps")
with(context.bytecodeContext) { // Accessing the lazy lookup maps to initialize them.
context.executablePatches.mergeExtensions() context.bytecodeContext.lookupMaps
// Initialize lookup maps.
lookupMaps
}
logger.info("Executing patches") logger.info("Executing patches")

View File

@ -59,42 +59,33 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
internal val lookupMaps by lazy { LookupMaps(classes) } internal val lookupMaps by lazy { LookupMaps(classes) }
/** /**
* Merge the extensions for this set of patches. * Merge the extension of this patch.
*/ */
internal fun Set<Patch<*>>.mergeExtensions() { internal fun BytecodePatch.mergeExtension() {
// Lookup map to check if a class exists by its type quickly. extension?.use { extensionStream ->
val classesByType = mutableMapOf<String, ClassDef>().apply { RawDexIO.readRawDexFile(extensionStream, 0, null).classes.forEach { classDef ->
classes.forEach { classDef -> put(classDef.type, classDef) } val existingClass = lookupMaps.classesByType[classDef.type] ?: run {
} logger.fine("Adding class \"$classDef\"")
forEachRecursively { patch -> classes += classDef
if (patch !is BytecodePatch) return@forEachRecursively lookupMaps.classesByType[classDef.type] = classDef
patch.extension?.use { extensionStream -> return@forEach
RawDexIO.readRawDexFile(extensionStream, 0, null).classes.forEach { classDef -> }
val existingClass = classesByType[classDef.type] ?: run {
logger.fine("Adding class \"$classDef\"")
classes += classDef logger.fine("Class \"$classDef\" exists already. Adding missing methods and fields.")
classesByType[classDef.type] = classDef
return@forEach existingClass.merge(classDef, this@BytecodePatchContext).let { mergedClass ->
// If the class was merged, replace the original class with the merged class.
if (mergedClass === existingClass) {
return@let
} }
logger.fine("Class \"$classDef\" exists already. Adding missing methods and fields.") classes -= existingClass
classes += mergedClass
existingClass.merge(classDef, this@BytecodePatchContext).let { mergedClass ->
// If the class was merged, replace the original class with the merged class.
if (mergedClass === existingClass) {
return@let
}
classes -= existingClass
classes += mergedClass
}
} }
} }
} } ?: return logger.fine("No extension to merge")
} }
/** /**
@ -185,6 +176,11 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
*/ */
internal val methodsByStrings = MethodClassPairsLookupMap() internal val methodsByStrings = MethodClassPairsLookupMap()
// Lookup map for fast checking if a class exists by its type.
val classesByType = mutableMapOf<String, ClassDef>().apply {
classes.forEach { classDef -> put(classDef.type, classDef) }
}
init { init {
classes.forEach { classDef -> classes.forEach { classDef ->
classDef.methods.forEach { method -> classDef.methods.forEach { method ->
@ -231,6 +227,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
override fun close() { override fun close() {
methodsByStrings.clear() methodsByStrings.clear()
classesByType.clear()
} }
} }

View File

@ -158,7 +158,13 @@ class BytecodePatch internal constructor(
finalizeBlock, finalizeBlock,
) { ) {
override fun execute(context: PatcherContext) = with(context.bytecodeContext) { override fun execute(context: PatcherContext) = with(context.bytecodeContext) {
fingerprints.forEach { it.match(this) } with(context.bytecodeContext) {
mergeExtension()
}
fingerprints.forEach {
it.match(this)
}
execute(this) execute(this)
} }

View File

@ -195,7 +195,7 @@ internal object PatcherTest {
private operator fun Set<Patch<*>>.invoke(): List<PatchResult> { private operator fun Set<Patch<*>>.invoke(): List<PatchResult> {
every { patcher.context.executablePatches } returns toMutableSet() every { patcher.context.executablePatches } returns toMutableSet()
every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classes) every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classes)
every { with(patcher.context.bytecodeContext) { any<Set<Patch<*>>>().mergeExtensions() } } just runs every { with(patcher.context.bytecodeContext) { any<BytecodePatch>().mergeExtension() } } just runs
return runBlocking { patcher().toList() } return runBlocking { patcher().toList() }
} }