From d5d6f85084c03ed9c776632823ca12394a716167 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 14 Jun 2023 02:14:37 +0200 Subject: [PATCH] fix: catch exceptions from closing patches --- .../kotlin/app/revanced/patcher/Patcher.kt | 47 +++++++++++++------ .../app/revanced/patcher/patch/Patch.kt | 9 +--- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 665f609f..24485921 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -23,6 +23,7 @@ import lanchon.multidexlib2.MultiDexIO import org.jf.dexlib2.Opcodes import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.writer.io.MemoryDataStore +import java.io.Closeable import java.io.File import java.io.OutputStream import java.nio.file.Files @@ -350,24 +351,42 @@ class Patcher(private val options: PatcherOptions) { val executedPatches = LinkedHashMap() // first is name - try { - context.patches.forEach { patch -> - val patchResult = executePatch(patch, executedPatches) - val result = if (patchResult.isSuccess()) { - Result.success(patchResult.success()!!) - } else { - Result.failure(patchResult.error()!!) - } + context.patches.forEach { patch -> + val patchResult = executePatch(patch, executedPatches) - yield(patch.patchName to result) - if (stopOnError && patchResult.isError()) return@sequence - } - } finally { - executedPatches.values.filter { it.success }.reversed().forEach { (patch, _) -> - patch.close() + val result = if (patchResult.isSuccess()) { + Result.success(patchResult.success()!!) + } else { + Result.failure(patchResult.error()!!) } + + // TODO: This prints before the patch really finishes in case it is a Closeable + // because the Closeable is closed after all patches are executed. + yield(patch.patchName to result) + + if (stopOnError && patchResult.isError()) return@sequence } + + executedPatches.values + .filter(ExecutedPatch::success) + .map(ExecutedPatch::patchInstance) + .filterIsInstance(Closeable::class.java) + .asReversed().forEach { + try { + it.close() + } catch (exception: Exception) { + val patchName = (it as Patch).javaClass.patchName + + logger.error("Failed to close '$patchName': ${exception.stackTraceToString()}") + + yield(patchName to Result.failure(exception)) + + // This is not failsafe. If a patch throws an exception while closing, + // the other patches that depend on it may fail. + if (stopOnError) return@sequence + } + } } } diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 8a194ed1..3f982b86 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -12,7 +12,7 @@ import java.io.Closeable * If it implements [Closeable], it will be closed after all patches have been executed. * Closing will be done in reverse execution order. */ -sealed interface Patch : Closeable { +sealed interface Patch { /** * The main function of the [Patch] which the patcher will call. * @@ -20,13 +20,6 @@ sealed interface Patch : Closeable { * @return The result of executing the patch. */ fun execute(context: @UnsafeVariance T): PatchResult - - /** - * The closing function for this patch. - * - * This can be treated like popping the patch from the current patch stack. - */ - override fun close() {} } /**