feat: Add callback for patch loading exceptions

This commit is contained in:
oSumAtrIX
2025-10-16 22:46:51 +02:00
parent 3a8b2ba935
commit e74498aff5
2 changed files with 98 additions and 52 deletions

View File

@@ -342,8 +342,12 @@ public final class app/revanced/patcher/patch/PatchKt {
public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
public static synthetic fun bytecodePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; public static synthetic fun bytecodePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;)Lapp/revanced/patcher/patch/PatchLoader$Dex; public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Dex; public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
public static final fun loadPatchesFromJar (Ljava/util/Set;)Lapp/revanced/patcher/patch/PatchLoader$Jar; public static final fun loadPatchesFromJar (Ljava/util/Set;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
public static final fun loadPatchesFromJar (Ljava/util/Set;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
public static synthetic fun loadPatchesFromJar$default (Ljava/util/Set;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
public static final fun rawResourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/RawResourcePatch; public static final fun rawResourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/RawResourcePatch;
public static synthetic fun rawResourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch; public static synthetic fun rawResourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch;
public static final fun resourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch; public static final fun resourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch;
@@ -352,7 +356,7 @@ public final class app/revanced/patcher/patch/PatchKt {
public abstract class app/revanced/patcher/patch/PatchLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker { public abstract class app/revanced/patcher/patch/PatchLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
public synthetic fun <init> (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/util/Set;Lkotlin/jvm/functions/Function1;Ljava/lang/ClassLoader;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Ljava/util/Set;Lkotlin/jvm/functions/Function1;Ljava/lang/ClassLoader;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun add (Lapp/revanced/patcher/patch/Patch;)Z public fun add (Lapp/revanced/patcher/patch/Patch;)Z
public synthetic fun add (Ljava/lang/Object;)Z public synthetic fun add (Ljava/lang/Object;)Z
public fun addAll (Ljava/util/Collection;)Z public fun addAll (Ljava/util/Collection;)Z
@@ -373,12 +377,9 @@ public abstract class app/revanced/patcher/patch/PatchLoader : java/util/Set, ko
} }
public final class app/revanced/patcher/patch/PatchLoader$Dex : app/revanced/patcher/patch/PatchLoader { public final class app/revanced/patcher/patch/PatchLoader$Dex : app/revanced/patcher/patch/PatchLoader {
public fun <init> (Ljava/util/Set;Ljava/io/File;)V
public synthetic fun <init> (Ljava/util/Set;Ljava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
} }
public final class app/revanced/patcher/patch/PatchLoader$Jar : app/revanced/patcher/patch/PatchLoader { public final class app/revanced/patcher/patch/PatchLoader$Jar : app/revanced/patcher/patch/PatchLoader {
public fun <init> (Ljava/util/Set;)V
} }
public final class app/revanced/patcher/patch/PatchResult { public final class app/revanced/patcher/patch/PatchResult {

View File

@@ -87,8 +87,7 @@ sealed class Patch<C : PatchContext<*>>(
finalizeBlock?.invoke(context) finalizeBlock?.invoke(context)
} }
override fun toString() = name ?: override fun toString() = name ?: "Patch@${System.identityHashCode(this)}"
"Patch@${System.identityHashCode(this)}"
} }
internal fun Patch<*>.anyRecursively( internal fun Patch<*>.anyRecursively(
@@ -529,11 +528,11 @@ fun resourcePatch(
/** /**
* An exception thrown when patching. * An exception thrown when patching.
* *
* @param errorMessage The exception message. * @param message The exception message.
* @param cause The corresponding [Throwable]. * @param cause The cause of the exception.
*/ */
class PatchException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) { class PatchException(message: String?, cause: Throwable?) : Exception(message, cause) {
constructor(errorMessage: String) : this(errorMessage, null) constructor(message: String) : this(message, null)
constructor(cause: Throwable) : this(cause.message, cause) constructor(cause: Throwable) : this(cause.message, cause)
} }
@@ -543,6 +542,7 @@ class PatchException(errorMessage: String?, cause: Throwable?) : Exception(error
* @param patch The [Patch] that was executed. * @param patch The [Patch] that was executed.
* @param exception The [PatchException] thrown, if any. * @param exception The [PatchException] thrown, if any.
*/ */
@Deprecated("This class is not used anymore. Instead a callback is used")
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null)
/** /**
@@ -553,35 +553,46 @@ class PatchResult internal constructor(val patch: Patch<*>, val exception: Patch
* *
* @param byPatchesFile The patches associated by the patches file they were loaded from. * @param byPatchesFile The patches associated by the patches file they were loaded from.
*/ */
sealed class PatchLoader private constructor( sealed class PatchLoader(
val byPatchesFile: Map<File, Set<Patch<*>>>, val byPatchesFile: Map<File, Set<Patch<*>>>,
) : Set<Patch<*>> by byPatchesFile.values.flatten().toSet() { ) : Set<Patch<*>> by byPatchesFile.values.flatten().toSet() {
/** /**
* @param patchesFiles A set of JAR or DEX files to load the patches from. * @param patchesFiles A set of JAR or DEX files to load the patches from.
* @param getBinaryClassNames A function that returns the binary names of all classes accessible by the class loader. * @param getBinaryClassNames A function that returns the binary names of all classes accessible by the class loader.
* @param classLoader The [ClassLoader] to use for loading the classes. * @param classLoader The [ClassLoader] to use for loading the classes.
* @param onLoadPatchesException The callback for patches that could not be loaded.
*/ */
private constructor( constructor(
patchesFiles: Set<File>, patchesFiles: Set<File>,
getBinaryClassNames: (patchesFile: File) -> List<String>, getBinaryClassNames: (patchesFile: File) -> List<String>,
classLoader: ClassLoader, classLoader: ClassLoader,
) : this(classLoader.loadPatches(patchesFiles.associateWith { getBinaryClassNames(it).toSet() })) onLoadPatchesException: (message: String, cause: Throwable) -> Unit
) : this(
classLoader.loadPatches(
patchesFiles.associateWith { getBinaryClassNames(it).toSet() },
onLoadPatchesException
)
)
/** /**
* A [PatchLoader] for JAR files. * A [PatchLoader] for JAR files.
* *
* @param patchesFiles The JAR files to load the patches from. * @param patchesFiles The JAR files to load the patches from.
* @param onLoadPatchesException The callback for patches that could not be loaded.
* *
* @constructor Create a new [PatchLoader] for JAR files. * @constructor Create a new [PatchLoader] for JAR files.
*/ */
class Jar(patchesFiles: Set<File>) : class Jar internal constructor(
PatchLoader( patchesFiles: Set<File>,
onLoadPatchesException: (message: String, cause: Throwable) -> Unit
) : PatchLoader(
patchesFiles, patchesFiles,
{ file -> { file ->
JarFile(file).entries().toList().filter { it.name.endsWith(".class") } JarFile(file).entries().toList().filter { it.name.endsWith(".class") }
.map { it.name.substringBeforeLast('.').replace('/', '.') } .map { it.name.substringBeforeLast('.').replace('/', '.') }
}, },
URLClassLoader(patchesFiles.map { it.toURI().toURL() }.toTypedArray()), URLClassLoader(patchesFiles.map { it.toURI().toURL() }.toTypedArray()),
onLoadPatchesException
) )
/** /**
@@ -590,11 +601,15 @@ sealed class PatchLoader private constructor(
* @param patchesFiles The DEX files to load the patches from. * @param patchesFiles The DEX files to load the patches from.
* @param optimizedDexDirectory The directory to store optimized DEX files in. * @param optimizedDexDirectory The directory to store optimized DEX files in.
* This parameter is deprecated and has no effect since API level 26. * This parameter is deprecated and has no effect since API level 26.
* @param onLoadPatchesException The callback for patches that could not be loaded.
* *
* @constructor Create a new [PatchLoader] for [Dex] files. * @constructor Create a new [PatchLoader] for [Dex] files.
*/ */
class Dex(patchesFiles: Set<File>, optimizedDexDirectory: File? = null) : class Dex internal constructor(
PatchLoader( patchesFiles: Set<File>,
optimizedDexDirectory: File? = null,
onLoadPatchesException: (message: String, cause: Throwable) -> Unit
) : PatchLoader(
patchesFiles, patchesFiles,
{ patchBundle -> { patchBundle ->
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
@@ -608,6 +623,7 @@ sealed class PatchLoader private constructor(
null, null,
this::class.java.classLoader, this::class.java.classLoader,
), ),
onLoadPatchesException
) )
// Companion object required for unit tests. // Companion object required for unit tests.
@@ -640,13 +656,18 @@ sealed class PatchLoader private constructor(
* *
* @param binaryClassNamesByPatchesFile The binary class name of the classes to load the patches from * @param binaryClassNamesByPatchesFile The binary class name of the classes to load the patches from
* associated by the patches file. * associated by the patches file.
* @param onLoadPatchesException The callback for patches that could not be loaded.
* *
* @return The loaded patches associated by the patches file. * @return The loaded patches associated by the patches file.
*/ */
private fun ClassLoader.loadPatches(binaryClassNamesByPatchesFile: Map<File, Set<String>>) = private fun ClassLoader.loadPatches(
binaryClassNamesByPatchesFile.mapValues { (_, binaryClassNames) -> binaryClassNamesByPatchesFile: Map<File, Set<String>>,
binaryClassNames.asSequence().map { onLoadPatchesException: (message: String, cause: Throwable) -> Unit
loadClass(it) ) = binaryClassNamesByPatchesFile.mapValues { (_, binaryClassNames) ->
binaryClassNames.asSequence().mapNotNull {
runCatching { loadClass(it) }.onFailure { exception ->
onLoadPatchesException("Failed to load patch class $it", exception)
}.getOrNull()
}.flatMap { }.flatMap {
it.patchFields + it.patchMethods it.patchFields + it.patchMethods
}.filter { }.filter {
@@ -668,11 +689,22 @@ sealed class PatchLoader private constructor(
* Patches with no name are not loaded. * Patches with no name are not loaded.
* *
* @param patchesFiles The JAR files to load the patches from. * @param patchesFiles The JAR files to load the patches from.
* @param onLoadPatchesException The callback for patches that could not be loaded.
* *
* @return The loaded patches. * @return The loaded patches.
*/ */
fun loadPatchesFromJar(patchesFiles: Set<File>) = fun loadPatchesFromJar(
PatchLoader.Jar(patchesFiles) patchesFiles: Set<File>,
onLoadPatchesException: ((message: String, cause: Throwable) -> Unit)? = null
) = PatchLoader.Jar(patchesFiles, onLoadPatchesException ?: { message, cause -> })
@Deprecated(
"Use the function with the onLoadPatchesException overload",
replaceWith = ReplaceWith("loadPatchesFromJar(patchesFiles, null)")
)
fun loadPatchesFromJar(
patchesFiles: Set<File>
) = loadPatchesFromJar(patchesFiles, null)
/** /**
* Loads patches from DEX files declared as public static fields * Loads patches from DEX files declared as public static fields
@@ -680,8 +712,21 @@ fun loadPatchesFromJar(patchesFiles: Set<File>) =
* Patches with no name are not loaded. * Patches with no name are not loaded.
* *
* @param patchesFiles The DEX files to load the patches from. * @param patchesFiles The DEX files to load the patches from.
* @param onLoadPatchesException The callback for patches that could not be loaded.
* *
* @return The loaded patches. * @return The loaded patches.
*/ */
fun loadPatchesFromDex(patchesFiles: Set<File>, optimizedDexDirectory: File? = null) = fun loadPatchesFromDex(
PatchLoader.Dex(patchesFiles, optimizedDexDirectory) patchesFiles: Set<File>,
optimizedDexDirectory: File? = null,
onLoadPatchesException: ((message: String, cause: Throwable) -> Unit)? = null
) = PatchLoader.Dex(patchesFiles, optimizedDexDirectory, onLoadPatchesException ?: { message, cause -> })
@Deprecated(
"Use the function with the onLoadPatchesException overload",
replaceWith = ReplaceWith("loadPatchesFromJar(patchesFiles, optimizedDexDirectory, null)")
)
fun loadPatchesFromDex(
patchesFiles: Set<File>,
optimizedDexDirectory: File? = null,
) = loadPatchesFromDex(patchesFiles, optimizedDexDirectory, null)