Compare commits

...

6 Commits

Author SHA1 Message Date
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
semantic-release-bot
77bbf6be1f chore(release): 6.1.1 [skip ci]
## [6.1.1](https://github.com/revanced/revanced-patcher/compare/v6.1.0...v6.1.1) (2022-11-25)

### Bug Fixes

* use `MethodUtil.methodSignaturesMatch` instead of `Method.softCompareTo` ([bd053b7](bd053b7e99))
2022-11-25 09:25:48 +00:00
oSumAtrIX
bd053b7e99 fix: use MethodUtil.methodSignaturesMatch instead of Method.softCompareTo 2022-11-25 10:24:13 +01:00
semantic-release-bot
fd742eba63 chore(release): 6.1.0 [skip ci]
# [6.1.0](https://github.com/revanced/revanced-patcher/compare/v6.0.2...v6.1.0) (2022-11-22)

### Features

* apply changes from ReVanced Patcher ([ba9d998](ba9d998681))
2022-11-22 23:30:33 +00:00
oSumAtrIX
ba9d998681 feat: apply changes from ReVanced Patcher
BREAKING-CHANGE: Some annotations have been removed regarding fingerprints and patches.
2022-11-23 00:29:12 +01:00
11 changed files with 115 additions and 99 deletions

View File

@@ -1,3 +1,24 @@
# [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)
### Bug Fixes
* use `MethodUtil.methodSignaturesMatch` instead of `Method.softCompareTo` ([bd053b7](https://github.com/revanced/revanced-patcher/commit/bd053b7e9974c0282d56e6762459db7070452e4a))
# [6.1.0](https://github.com/revanced/revanced-patcher/compare/v6.0.2...v6.1.0) (2022-11-22)
### Features
* apply changes from ReVanced Patcher ([ba9d998](https://github.com/revanced/revanced-patcher/commit/ba9d99868103406fe36b9aa0cfaa0ed5023edfab))
## [6.0.2](https://github.com/revanced/revanced-patcher/compare/v6.0.1...v6.0.2) (2022-11-18) ## [6.0.2](https://github.com/revanced/revanced-patcher/compare/v6.0.1...v6.0.2) (2022-11-18)

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 6.0.2 version = 6.2.0

View File

@@ -3,12 +3,15 @@ package app.revanced.patcher
import app.revanced.patcher.data.Context import app.revanced.patcher.data.Context
import app.revanced.patcher.data.findIndexed import app.revanced.patcher.data.findIndexed
import app.revanced.patcher.extensions.PatchExtensions.dependencies import app.revanced.patcher.extensions.PatchExtensions.dependencies
import app.revanced.patcher.extensions.PatchExtensions.deprecated
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.extensions.nullOutputStream 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
@@ -23,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
@@ -66,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)
} }
} }
@@ -155,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")
} }
@@ -241,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")
@@ -316,11 +368,6 @@ class Patcher(private val options: PatcherOptions) {
return PatchResultError("'$patchName' depends on '${dependency.patchName}' but the following error was raised: $errorMessage") return PatchResultError("'$patchName' depends on '${dependency.patchName}' but the following error was raised: $errorMessage")
} }
patchClass.deprecated?.let { (reason, replacement) ->
logger.warn("'$patchName' is deprecated, reason: $reason")
if (replacement != null) logger.warn("Use '${replacement.java.patchName}' instead")
}
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass) val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
val patchInstance = patchClass.getDeclaredConstructor().newInstance() val patchInstance = patchClass.getDeclaredConstructor().newInstance()

View File

@@ -1,11 +1,10 @@
package app.revanced.patcher.annotation package app.revanced.patcher.annotation
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
/** /**
* Annotation to constrain a [Patch] or [MethodFingerprint] to compatible packages. * Annotation to constrain a [Patch] to compatible packages.
* @param compatiblePackages A list of packages a [Patch] or [MethodFingerprint] is compatible with. * @param compatiblePackages A list of packages a [Patch] is compatible with.
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@@ -17,7 +16,7 @@ annotation class Compatibility(
/** /**
* Annotation to represent packages a patch can be compatible with. * Annotation to represent packages a patch can be compatible with.
* @param name The package identifier name. * @param name The package identifier name.
* @param versions The versions of the package the [Patch] or [MethodFingerprint]is compatible with. * @param versions The versions of the package the [Patch] is compatible with.
*/ */
@Target() @Target()
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)

View File

@@ -1,19 +0,0 @@
package app.revanced.patcher.annotation
import app.revanced.patcher.data.Context
import app.revanced.patcher.patch.Patch
import kotlin.reflect.KClass
/**
* Declares a [Patch] deprecated for removal.
* @param reason The reason why the patch is deprecated.
* @param replacement The replacement for the deprecated patch, if any.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class PatchDeprecated(
val reason: String,
val replacement: KClass<out Patch<Context>> = Patch::class
// Values cannot be nullable in annotations, so this will have to do.
)

View File

@@ -1,11 +1,10 @@
package app.revanced.patcher.annotation package app.revanced.patcher.annotation
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
/** /**
* Annotation to name a [Patch] or [MethodFingerprint]. * Annotation to name a [Patch].
* @param name A suggestive name for the [Patch] or [MethodFingerprint]. * @param name A suggestive name for the [Patch].
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@@ -15,8 +14,8 @@ annotation class Name(
) )
/** /**
* Annotation to describe a [Patch] or [MethodFingerprint]. * Annotation to describe a [Patch].
* @param description A description for the [Patch] or [MethodFingerprint]. * @param description A description for the [Patch].
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@@ -27,8 +26,8 @@ annotation class Description(
/** /**
* Annotation to version a [Patch] or [MethodFingerprint]. * Annotation to version a [Patch].
* @param version The version of a [Patch] or [MethodFingerprint]. * @param version The version of a [Patch].
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)

View File

@@ -65,24 +65,11 @@ object PatchExtensions {
(it as? OptionsContainer)?.options (it as? OptionsContainer)?.options
} }
} }
val Class<out Patch<Context>>.deprecated: Pair<String, KClass<out Patch<Context>>?>?
get() = findAnnotationRecursively(PatchDeprecated::class)?.let {
it.reason to it.replacement.let { cl ->
if (cl == Patch::class) null else cl
}
}
} }
object MethodFingerprintExtensions { object MethodFingerprintExtensions {
val MethodFingerprint.name: String val MethodFingerprint.name: String
get() = javaClass.findAnnotationRecursively(Name::class)?.name ?: this.javaClass.simpleName get() = this.javaClass.simpleName
val MethodFingerprint.version
get() = javaClass.findAnnotationRecursively(Version::class)?.version ?: "0.0.1"
val MethodFingerprint.description
get() = javaClass.findAnnotationRecursively(Description::class)?.description
val MethodFingerprint.fuzzyPatternScanMethod val MethodFingerprint.fuzzyPatternScanMethod
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class) get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)

View File

@@ -13,7 +13,6 @@ import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.* import org.jf.dexlib2.builder.instruction.*
import org.jf.dexlib2.iface.Method import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.reference.MethodReference
import org.jf.dexlib2.immutable.ImmutableMethod import org.jf.dexlib2.immutable.ImmutableMethod
import org.jf.dexlib2.immutable.ImmutableMethodImplementation import org.jf.dexlib2.immutable.ImmutableMethodImplementation
import java.io.OutputStream import java.io.OutputStream
@@ -45,17 +44,6 @@ fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
} }
} }
/**
* Compare a method to another, considering name and parameters.
* @param otherMethod The method to compare against.
* @return True if the methods match given the conditions.
*/
fun Method.softCompareTo(otherMethod: MethodReference): Boolean {
return this.name == otherMethod.name && parametersEqual(
this.parameterTypes, otherMethod.parameterTypes
)
}
/** /**
* Clones the method. * Clones the method.
* @param registerCount This parameter allows you to change the register count of the method. * @param registerCount This parameter allows you to change the register count of the method.

View File

@@ -4,7 +4,6 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold
import app.revanced.patcher.extensions.parametersEqual import app.revanced.patcher.extensions.parametersEqual
import app.revanced.patcher.extensions.softCompareTo
import app.revanced.patcher.fingerprint.Fingerprint import app.revanced.patcher.fingerprint.Fingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.util.proxy.ClassProxy import app.revanced.patcher.util.proxy.ClassProxy
@@ -14,6 +13,7 @@ import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.StringReference import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.util.MethodUtil
/** /**
* Represents the [MethodFingerprint] for a method. * Represents the [MethodFingerprint] for a method.
@@ -297,7 +297,7 @@ data class MethodFingerprintResult(
*/ */
val mutableMethod by lazy { val mutableMethod by lazy {
mutableClass.methods.first { mutableClass.methods.first {
it.softCompareTo(this.method) MethodUtil.methodSignaturesMatch(it, this.method)
} }
} }
} }

View File

@@ -1,11 +1,11 @@
package app.revanced.patcher.util.method package app.revanced.patcher.util.method
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.softCompareTo
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.iface.Method import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.ReferenceInstruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.MethodReference import org.jf.dexlib2.iface.reference.MethodReference
import org.jf.dexlib2.util.MethodUtil
/** /**
* Find a method from another method via instruction offsets. * Find a method from another method via instruction offsets.
@@ -44,8 +44,8 @@ class MethodWalker internal constructor(
val proxy = bytecodeContext.findClass(newMethod.definingClass)!! val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
currentMethod = methods.first { it -> currentMethod = methods.first {
return@first it.softCompareTo(newMethod) return@first MethodUtil.methodSignaturesMatch(it, newMethod)
} }
return this return this
} }

View File

@@ -1,17 +1,11 @@
package app.revanced.patcher.usage.bytecode package app.revanced.patcher.usage.bytecode
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode import org.jf.dexlib2.Opcode
@Name("example-fingerprint")
@FuzzyPatternScanMethod(2) @FuzzyPatternScanMethod(2)
@ExampleBytecodeCompatibility
@Version("0.0.1")
object ExampleFingerprint : MethodFingerprint( object ExampleFingerprint : MethodFingerprint(
"V", "V",
AccessFlags.PUBLIC or AccessFlags.STATIC, AccessFlags.PUBLIC or AccessFlags.STATIC,