Compare commits

..

4 Commits

Author SHA1 Message Date
semantic-release-bot
ea773cfa56 chore(release): 6.3.0 [skip ci]
# [6.3.0](https://github.com/revanced/revanced-patcher/compare/v6.2.0...v6.3.0) (2022-12-02)

### Features

* sort patches in lexicographical order ([a306561](a306561b55)), closes [#125](https://github.com/revanced/revanced-patcher/issues/125)
2022-12-02 02:32:49 +00:00
oSumAtrIX
a306561b55 feat: sort patches in lexicographical order
Closes #125
2022-12-02 03:31:25 +01:00
semantic-release-bot
b6dcd88495 chore(release): 6.2.0 [skip ci]
# [6.2.0](https://github.com/revanced/revanced-patcher/compare/v6.1.1...v6.2.0) (2022-12-02)

### Features

* merge classes on addition ([#127](https://github.com/revanced/revanced-patcher/issues/127)) ([a925650](a925650044))
2022-12-02 01:33:08 +00:00
oSumAtrIX
a925650044 feat: merge classes on addition (#127) 2022-12-02 02:31:47 +01:00
4 changed files with 97 additions and 29 deletions

View File

@@ -1,3 +1,17 @@
# [6.3.0](https://github.com/revanced/revanced-patcher/compare/v6.2.0...v6.3.0) (2022-12-02)
### Features
* sort patches in lexicographical order ([a306561](https://github.com/revanced/revanced-patcher/commit/a306561b55ac848792046378f582a036f7ffab03)), closes [#125](https://github.com/revanced/revanced-patcher/issues/125)
# [6.2.0](https://github.com/revanced/revanced-patcher/compare/v6.1.1...v6.2.0) (2022-12-02)
### Features
* merge classes on addition ([#127](https://github.com/revanced/revanced-patcher/issues/127)) ([a925650](https://github.com/revanced/revanced-patcher/commit/a9256500440f9b4117f1b8813ba0097dafee4ebb))
## [6.1.1](https://github.com/revanced/revanced-patcher/compare/v6.1.0...v6.1.1) (2022-11-25) ## [6.1.1](https://github.com/revanced/revanced-patcher/compare/v6.1.0...v6.1.1) (2022-11-25)

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 6.1.1 version = 6.3.0

View File

@@ -8,6 +8,10 @@ import app.revanced.patcher.extensions.nullOutputStream
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.* import app.revanced.patcher.patch.*
import app.revanced.patcher.util.VersionReader import app.revanced.patcher.util.VersionReader
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.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import brut.androlib.Androlib import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework import brut.androlib.meta.UsesFramework
import brut.androlib.options.BuildOptions import brut.androlib.options.BuildOptions
@@ -22,7 +26,9 @@ import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO import lanchon.multidexlib2.DexIO
import lanchon.multidexlib2.MultiDexIO import lanchon.multidexlib2.MultiDexIO
import org.jf.dexlib2.Opcodes import org.jf.dexlib2.Opcodes
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.iface.DexFile
import org.jf.dexlib2.util.MethodUtil
import org.jf.dexlib2.writer.io.MemoryDataStore import org.jf.dexlib2.writer.io.MemoryDataStore
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
@@ -65,40 +71,85 @@ class Patcher(private val options: PatcherOptions) {
/** /**
* Add additional dex file container to the patcher. * Add additional dex file container to the patcher.
* @param files The dex file containers to add to the patcher. * @param files The dex file containers to add to the patcher.
* @param allowedOverwrites A list of class types that are allowed to be overwritten. * @param process The callback for [files] which are being added.
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
*/ */
fun addFiles( fun addFiles(
files: List<File>, files: List<File>,
allowedOverwrites: Iterable<String> = emptyList(), process: (File) -> Unit
throwOnDuplicates: Boolean = false,
callback: (File) -> Unit
) { ) {
for (file in files) { with(context.bytecodeContext.classes) {
var modified = false for (file in files) {
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) { process(file)
val type = classDef.type for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
val type = classDef.type
val existingClass = context.bytecodeContext.classes.classes.findIndexed { it.type == type } val result = classes.findIndexed { it.type == type }
if (existingClass == null) { if (result == null) {
if (throwOnDuplicates) throw Exception("Class $type has already been added to the patcher") logger.trace("Merging type $type")
classes.add(classDef)
} else {
val (existingClass, existingClassIndex) = result
logger.trace("Merging $type") logger.trace("Type $type exists. Adding missing methods and fields.")
context.bytecodeContext.classes.classes.add(classDef)
modified = true
continue /**
* Add missing fields and methods from [from].
*
* @param from The class to add methods and fields from.
*/
fun ClassDef.addMissingFrom(from: ClassDef) {
var changed = false
fun <T> ClassDef.transformClass(transform: (MutableClass) -> T): T {
fun toMutableClass() =
if (this@transformClass is MutableClass) this else this.toMutable()
return transform(toMutableClass())
}
fun ClassDef.addMissingMethods(): ClassDef {
fun getMissingMethods() = from.methods.filterNot {
this@addMissingMethods.methods.any { original ->
MethodUtil.methodSignaturesMatch(original, it)
}
}
return getMissingMethods()
.apply {
if (isEmpty()) return@addMissingMethods this@addMissingMethods else changed =
true
}
.map { it.toMutable() }
.let { missingMethods ->
this@addMissingMethods.transformClass { classDef ->
classDef.apply { methods.addAll(missingMethods) }
}
}
}
fun ClassDef.addMissingFields(): ClassDef {
fun getMissingFields() = from.fields.filterNot {
this@addMissingFields.fields.any { original -> original.name == it.name }
}
return getMissingFields()
.apply {
if (isEmpty()) return@addMissingFields this@addMissingFields else changed = true
}
.map { it.toMutable() }
.let { missingFields ->
this@addMissingFields.transformClass { classDef ->
classDef.apply { fields.addAll(missingFields) }
}
}
}
classes[existingClassIndex] = addMissingMethods().addMissingFields()
.apply { if (!changed) return }
}
existingClass.addMissingFrom(classDef)
}
} }
if (!allowedOverwrites.contains(type)) continue
logger.trace("Overwriting $type")
val index = existingClass.second
context.bytecodeContext.classes.classes[index] = classDef
modified = true
} }
if (modified) callback(file)
} }
} }
@@ -154,6 +205,7 @@ class Patcher(private val options: PatcherOptions) {
cacheDirectory.close() cacheDirectory.close()
} }
} }
else -> logger.info("Not compiling resources because resource patching is not required") else -> logger.info("Not compiling resources because resource patching is not required")
} }
@@ -240,6 +292,7 @@ class Patcher(private val options: PatcherOptions) {
} }
} }
ResourceDecodingMode.MANIFEST_ONLY -> { ResourceDecodingMode.MANIFEST_ONLY -> {
logger.info("Decoding AndroidManifest.xml only, because resources are not needed") logger.info("Decoding AndroidManifest.xml only, because resources are not needed")

View File

@@ -3,6 +3,7 @@
package app.revanced.patcher.util.patch package app.revanced.patcher.util.patch
import app.revanced.patcher.data.Context import app.revanced.patcher.data.Context
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import org.jf.dexlib2.DexFileFactory import org.jf.dexlib2.DexFileFactory
import java.io.File import java.io.File
@@ -16,12 +17,12 @@ import java.util.jar.JarFile
*/ */
sealed class PatchBundle(path: String) : File(path) { sealed class PatchBundle(path: String) : File(path) {
internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList { internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList {
for (className in classNames) { classNames.forEach { className ->
val clazz = classLoader.loadClass(className) val clazz = classLoader.loadClass(className)
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) continue if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach
@Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>) @Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>)
} }
} }.sortedBy { it.patchName }
/** /**
* A patch bundle of type [Jar]. * A patch bundle of type [Jar].