Skip to content

Commit

Permalink
feat: use annotations instead of metadata objects
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 ebbcf78 commit d20f7fd
Show file tree
Hide file tree
Showing 54 changed files with 444 additions and 333 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Expand Up @@ -29,6 +29,7 @@ dependencies {
api("org.smali:smali:2.5.2")

testImplementation(kotlin("test"))
implementation(kotlin("reflect"))
}

tasks {
Expand Down
25 changes: 18 additions & 7 deletions src/main/kotlin/app/revanced/patcher/Patcher.kt
@@ -1,15 +1,16 @@
package app.revanced.patcher

import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.PatcherData
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.data.implementation.findIndexed
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.ResourcePatch
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.resolver.SignatureResolver
import app.revanced.patcher.signature.implementation.method.MethodSignature
import app.revanced.patcher.signature.implementation.method.resolver.MethodSignatureResolver
import app.revanced.patcher.util.ListBackedSet
import brut.androlib.Androlib
import brut.androlib.meta.UsesFramework
Expand Down Expand Up @@ -159,11 +160,12 @@ class Patcher(
return emptyList()
}

SignatureResolver(patcherData.bytecodeData.classes.internalClasses, signatures).resolve(patcherData)
MethodSignatureResolver(patcherData.bytecodeData.classes.internalClasses, signatures).resolve(patcherData)
signaturesResolved = true
return signatures
}


/**
* Apply patches loaded into the patcher.
* @param stopOnError If true, the patches will stop on the first error.
Expand All @@ -174,7 +176,7 @@ class Patcher(
fun applyPatches(
stopOnError: Boolean = false,
callback: (String) -> Unit = {}
): Map<PatchMetadata, Result<PatchResultSuccess>> {
): Map<String, Result<PatchResultSuccess>> {
if (!signaturesResolved) {
resolveSignatures()
}
Expand All @@ -183,7 +185,12 @@ class Patcher(
val resourcePatch = patch is ResourcePatch
if (!patchResources && resourcePatch) continue

callback(patch.metadata.shortName)
val patchNameAnnotation = patch::class.java.findAnnotationRecursively(Name::class.java)

patchNameAnnotation?.let {
callback(it.name)
}

val result: Result<PatchResultSuccess> = try {
val data = if (resourcePatch) {
patcherData.resourceData
Expand All @@ -201,7 +208,11 @@ class Patcher(
} catch (e: Exception) {
Result.failure(e)
}
this[patch.metadata] = result

patchNameAnnotation?.let {
this[patchNameAnnotation.name] = result
}

if (result.isFailure && stopOnError) break
}
}
Expand Down
@@ -0,0 +1,28 @@
package app.revanced.patcher.annotation

import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.signature.implementation.method.MethodSignature

/**
* Annotation to constrain a [Patch] or [MethodSignature] to compatible packages.
* @param compatiblePackages A list of packages a [Patch] or [MethodSignature] is compatible with.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Compatibility(
val compatiblePackages: Array<Package>,
)

/**
* Annotation to represent packages a patch can be compatible with.
* @param name The package identifier name.
* @param versions The versions of the package the [Patch] or [MethodSignature]is compatible with.
*/
@Target()
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Package(
val name: String,
val versions: Array<String>
)
@@ -0,0 +1,38 @@
package app.revanced.patcher.annotation

import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.signature.implementation.method.MethodSignature

/**
* Annotation to name a [Patch] or [MethodSignature].
* @param name A suggestive name for the [Patch] or [MethodSignature].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Name(
val name: String,
)

/**
* Annotation to describe a [Patch] or [MethodSignature].
* @param description A description for the [Patch] or [MethodSignature].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Description(
val description: String,
)


/**
* Annotation to version a [Patch] or [MethodSignature].
* @param version The version of a [Patch] or [MethodSignature].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Version(
val version: String,
)
@@ -1,12 +1,11 @@
package app.revanced.patcher.data.implementation

import app.revanced.patcher.data.base.Data
import app.revanced.patcher.methodWalker.MethodWalker
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.proxy.ClassProxy
import app.revanced.patcher.signature.SignatureResolverResult
import app.revanced.patcher.signature.implementation.method.resolver.SignatureResolverResult
import app.revanced.patcher.util.ProxyBackedClassList
import app.revanced.patcher.util.method.MethodWalker
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method

Expand All @@ -28,7 +27,7 @@ class BytecodeData(
* Find a class by a given predicate
* @return A proxy for the first class that matches the predicate
*/
fun findClass(predicate: (ClassDef) -> Boolean): ClassProxy? {
fun findClass(predicate: (ClassDef) -> Boolean): app.revanced.patcher.util.proxy.ClassProxy? {
// if we already proxied the class matching the predicate...
for (patch in patches) {
if (patch !is BytecodePatch) continue
Expand Down Expand Up @@ -77,10 +76,10 @@ internal inline fun <T> Iterable<T>.findIndexed(predicate: (T) -> Boolean): Pair
return null
}

fun BytecodeData.proxy(classDef: ClassDef): ClassProxy {
fun BytecodeData.proxy(classDef: ClassDef): app.revanced.patcher.util.proxy.ClassProxy {
var proxy = this.classes.proxies.find { it.immutableClass.type == classDef.type }
if (proxy == null) {
proxy = ClassProxy(classDef)
proxy = app.revanced.patcher.util.proxy.ClassProxy(classDef)
this.classes.proxies.add(proxy)
}
return proxy
Expand Down
29 changes: 28 additions & 1 deletion src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt
@@ -1,6 +1,6 @@
package app.revanced.patcher.extensions

import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.builder.MutableMethodImplementation
Expand All @@ -10,6 +10,33 @@ import org.jf.dexlib2.immutable.ImmutableMethod
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
import org.jf.dexlib2.util.MethodUtil

/**
* Recursively find a given annotation on a class
* @param targetAnnotation The annotation to find
* @return The annotation
*/
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: Class<T>) =
this.findAnnotationRecursively(targetAnnotation, mutableSetOf())

private fun <T : Annotation> Class<*>.findAnnotationRecursively(
targetAnnotation: Class<T>,
traversed: MutableSet<Annotation>
): T? {
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }

@Suppress("UNCHECKED_CAST")
if (found != null) return found as T

for (annotation in this.annotations) {
if (traversed.contains(annotation)) continue
traversed.add(annotation)

return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue
}

return null
}

infix fun AccessFlags.or(other: AccessFlags) = this.value or other.value
infix fun Int.or(other: AccessFlags) = this or other.value

Expand Down
@@ -0,0 +1,9 @@
package app.revanced.patcher.patch.annotations

/**
* Annotation to mark a Class as a patch.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Patch
8 changes: 2 additions & 6 deletions src/main/kotlin/app/revanced/patcher/patch/base/Patch.kt
Expand Up @@ -3,20 +3,16 @@ package app.revanced.patcher.patch.base
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.ResourcePatch
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult


/**
* A ReVanced patch.
* Can either be a [ResourcePatch] or a [BytecodePatch]
* Can either be a [ResourcePatch] or a [BytecodePatch].
*/
abstract class Patch<out T : Data>(
open val metadata: PatchMetadata
) {
abstract class Patch<out T : Data> {
/**
* The main function of the [Patch] which the patcher will call.
*/
abstract fun execute(data: @UnsafeVariance T): PatchResult // FIXME: remove the UnsafeVariance annotation

}
Expand Up @@ -2,15 +2,12 @@ package app.revanced.patcher.patch.implementation

import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.implementation.method.MethodSignature

/**
* Bytecode patch for the Patcher.
* @param metadata [PatchMetadata] for the patch.
* @param signatures A list of [MethodSignature] this patch relies on.
*/
abstract class BytecodePatch(
override val metadata: PatchMetadata,
val signatures: Iterable<MethodSignature>
) : Patch<BytecodeData>(metadata)
) : Patch<BytecodeData>()
Expand Up @@ -2,12 +2,8 @@ package app.revanced.patcher.patch.implementation

import app.revanced.patcher.data.implementation.ResourceData
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata

/**
* Resource patch for the Patcher.
* @param metadata [PatchMetadata] for the patch.
*/
abstract class ResourcePatch(
override val metadata: PatchMetadata
) : Patch<ResourceData>(metadata)
abstract class ResourcePatch : Patch<ResourceData>()

This file was deleted.

0 comments on commit d20f7fd

Please sign in to comment.