mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-12 06:16:18 +00:00
Compare commits
8 Commits
v21.1.0-de
...
feat/load-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
926ec96291 | ||
|
|
e74498aff5 | ||
|
|
3a8b2ba935 | ||
|
|
39c5a66ce3 | ||
|
|
b160a2adc0 | ||
|
|
33fadcbd0c | ||
|
|
68db95b99b | ||
|
|
4f2ef3c47c |
4
.github/workflows/build_pull_request.yml
vendored
4
.github/workflows/build_pull_request.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
uses: burrunan/gradle-cache-action@v3
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
||||
2
.github/workflows/open_pull_request.yml
vendored
2
.github/workflows/open_pull_request.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Open pull request
|
||||
uses: repo-sync/pull-request@v2
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
# Make sure the release step uses its own credentials:
|
||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
uses: burrunan/gradle-cache-action@v3
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
run: ./gradlew build clean
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: 'npm'
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
# [21.1.0-dev.5](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.4...v21.1.0-dev.5) (2025-10-16)
|
||||
|
||||
# [21.1.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.3...v21.1.0-dev.4) (2025-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Correctly save XML files in UTF-8 by using a bufferedWriter ([#356](https://github.com/ReVanced/revanced-patcher/issues/356)) ([33fadcb](https://github.com/ReVanced/revanced-patcher/commit/33fadcbd0c7076b848bdca4d62a9c684d5781232))
|
||||
|
||||
# [21.1.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.2...v21.1.0-dev.3) (2025-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Encode XML files as UTF-8 to fix compilation of resources ([#339](https://github.com/ReVanced/revanced-patcher/issues/339)) ([4f2ef3c](https://github.com/ReVanced/revanced-patcher/commit/4f2ef3c47cea76a26c464cfb45d4bb57fe7198b5))
|
||||
|
||||
# [21.1.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.1...v21.1.0-dev.2) (2025-06-20)
|
||||
|
||||
|
||||
|
||||
@@ -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 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;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;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;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 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;
|
||||
@@ -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 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 synthetic fun add (Ljava/lang/Object;)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 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 fun <init> (Ljava/util/Set;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchResult {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
version = 21.1.0-dev.2
|
||||
version = 21.1.0-dev.5
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
android = "4.1.1.4"
|
||||
apktool-lib = "2.10.1.1"
|
||||
binary-compatibility-validator = "0.15.1"
|
||||
binary-compatibility-validator = "0.18.1"
|
||||
kotlin = "2.0.20"
|
||||
kotlinx-coroutines-core = "1.8.1"
|
||||
mockk = "1.13.10"
|
||||
kotlinx-coroutines-core = "1.10.2"
|
||||
mockk = "1.14.5"
|
||||
multidexlib2 = "3.0.3.r3"
|
||||
# Tracking https://github.com/google/smali/issues/64.
|
||||
#noinspection GradleDependency
|
||||
smali = "3.0.8"
|
||||
smali = "3.0.9"
|
||||
xpp3 = "1.1.4c"
|
||||
|
||||
[libraries]
|
||||
|
||||
1391
package-lock.json
generated
1391
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,6 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1",
|
||||
"semantic-release": "^24.1.2"
|
||||
"semantic-release": "^24.2.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ sealed class Patch<C : PatchContext<*>>(
|
||||
finalizeBlock?.invoke(context)
|
||||
}
|
||||
|
||||
override fun toString() = name ?:
|
||||
"Patch@${System.identityHashCode(this)}"
|
||||
override fun toString() = name ?: "Patch@${System.identityHashCode(this)}"
|
||||
}
|
||||
|
||||
internal fun Patch<*>.anyRecursively(
|
||||
@@ -529,11 +528,11 @@ fun resourcePatch(
|
||||
/**
|
||||
* An exception thrown when patching.
|
||||
*
|
||||
* @param errorMessage The exception message.
|
||||
* @param cause The corresponding [Throwable].
|
||||
* @param message The exception message.
|
||||
* @param cause The cause of the exception.
|
||||
*/
|
||||
class PatchException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
||||
constructor(errorMessage: String) : this(errorMessage, null)
|
||||
class PatchException(message: String?, cause: Throwable?) : Exception(message, cause) {
|
||||
constructor(message: String) : this(message, null)
|
||||
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 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)
|
||||
|
||||
/**
|
||||
@@ -553,36 +553,47 @@ class PatchResult internal constructor(val patch: Patch<*>, val exception: Patch
|
||||
*
|
||||
* @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<*>>>,
|
||||
) : Set<Patch<*>> by byPatchesFile.values.flatten().toSet() {
|
||||
/**
|
||||
* @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 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>,
|
||||
getBinaryClassNames: (patchesFile: File) -> List<String>,
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
class Jar(patchesFiles: Set<File>) :
|
||||
PatchLoader(
|
||||
patchesFiles,
|
||||
{ file ->
|
||||
JarFile(file).entries().toList().filter { it.name.endsWith(".class") }
|
||||
.map { it.name.substringBeforeLast('.').replace('/', '.') }
|
||||
},
|
||||
URLClassLoader(patchesFiles.map { it.toURI().toURL() }.toTypedArray()),
|
||||
)
|
||||
class Jar internal constructor(
|
||||
patchesFiles: Set<File>,
|
||||
onLoadPatchesException: (message: String, cause: Throwable) -> Unit
|
||||
) : PatchLoader(
|
||||
patchesFiles,
|
||||
{ file ->
|
||||
JarFile(file).entries().toList().filter { it.name.endsWith(".class") }
|
||||
.map { it.name.substringBeforeLast('.').replace('/', '.') }
|
||||
},
|
||||
URLClassLoader(patchesFiles.map { it.toURI().toURL() }.toTypedArray()),
|
||||
onLoadPatchesException
|
||||
)
|
||||
|
||||
/**
|
||||
* A [PatchLoader] for [Dex] files.
|
||||
@@ -590,25 +601,30 @@ sealed class PatchLoader private constructor(
|
||||
* @param patchesFiles The DEX files to load the patches from.
|
||||
* @param optimizedDexDirectory The directory to store optimized DEX files in.
|
||||
* 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.
|
||||
*/
|
||||
class Dex(patchesFiles: Set<File>, optimizedDexDirectory: File? = null) :
|
||||
PatchLoader(
|
||||
patchesFiles,
|
||||
{ patchBundle ->
|
||||
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
||||
.map { classDef ->
|
||||
classDef.type.substring(1, classDef.length - 1)
|
||||
}
|
||||
},
|
||||
DexClassLoader(
|
||||
patchesFiles.joinToString(File.pathSeparator) { it.absolutePath },
|
||||
optimizedDexDirectory?.absolutePath,
|
||||
null,
|
||||
this::class.java.classLoader,
|
||||
),
|
||||
)
|
||||
class Dex internal constructor(
|
||||
patchesFiles: Set<File>,
|
||||
optimizedDexDirectory: File? = null,
|
||||
onLoadPatchesException: (message: String, cause: Throwable) -> Unit
|
||||
) : PatchLoader(
|
||||
patchesFiles,
|
||||
{ patchBundle ->
|
||||
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
||||
.map { classDef ->
|
||||
classDef.type.substring(1, classDef.length - 1)
|
||||
}
|
||||
},
|
||||
DexClassLoader(
|
||||
patchesFiles.joinToString(File.pathSeparator) { it.absolutePath },
|
||||
optimizedDexDirectory?.absolutePath,
|
||||
null,
|
||||
this::class.java.classLoader,
|
||||
),
|
||||
onLoadPatchesException
|
||||
)
|
||||
|
||||
// Companion object required for unit tests.
|
||||
private companion object {
|
||||
@@ -640,19 +656,24 @@ sealed class PatchLoader private constructor(
|
||||
*
|
||||
* @param binaryClassNamesByPatchesFile The binary class name of the classes to load the patches from
|
||||
* 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.
|
||||
*/
|
||||
private fun ClassLoader.loadPatches(binaryClassNamesByPatchesFile: Map<File, Set<String>>) =
|
||||
binaryClassNamesByPatchesFile.mapValues { (_, binaryClassNames) ->
|
||||
binaryClassNames.asSequence().map {
|
||||
loadClass(it)
|
||||
}.flatMap {
|
||||
it.patchFields + it.patchMethods
|
||||
}.filter {
|
||||
it.name != null
|
||||
}.toSet()
|
||||
}
|
||||
private fun ClassLoader.loadPatches(
|
||||
binaryClassNamesByPatchesFile: Map<File, Set<String>>,
|
||||
onLoadPatchesException: (message: String, cause: Throwable) -> Unit
|
||||
) = binaryClassNamesByPatchesFile.mapValues { (_, binaryClassNames) ->
|
||||
binaryClassNames.asSequence().mapNotNull {
|
||||
runCatching { loadClass(it) }.onFailure { exception ->
|
||||
onLoadPatchesException("Failed to load patch class $it", exception)
|
||||
}.getOrNull()
|
||||
}.flatMap {
|
||||
it.patchFields + it.patchMethods
|
||||
}.filter {
|
||||
it.name != null
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun Member.canAccess(): Boolean {
|
||||
if (this is Method && parameterCount != 0) return false
|
||||
@@ -668,11 +689,22 @@ sealed class PatchLoader private constructor(
|
||||
* Patches with no name are not loaded.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
fun loadPatchesFromJar(patchesFiles: Set<File>) =
|
||||
PatchLoader.Jar(patchesFiles)
|
||||
fun loadPatchesFromJar(
|
||||
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
|
||||
@@ -680,8 +712,21 @@ fun loadPatchesFromJar(patchesFiles: Set<File>) =
|
||||
* Patches with no name are not loaded.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
fun loadPatchesFromDex(patchesFiles: Set<File>, optimizedDexDirectory: File? = null) =
|
||||
PatchLoader.Dex(patchesFiles, optimizedDexDirectory)
|
||||
fun loadPatchesFromDex(
|
||||
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)
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
@@ -34,15 +35,22 @@ class Document internal constructor(
|
||||
readerCount.remove(it)
|
||||
}
|
||||
|
||||
it.outputStream().buffered().use { stream ->
|
||||
TransformerFactory.newInstance()
|
||||
.newTransformer()
|
||||
.transform(DOMSource(this), StreamResult(stream))
|
||||
val transformer = TransformerFactory.newInstance().newTransformer()
|
||||
// Set to UTF-16 to prevent surrogate pairs from being escaped to invalid numeric character references, but save as UTF-8.
|
||||
if (isAndroid) {
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-16")
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
|
||||
it.bufferedWriter(charset = Charsets.UTF_8).use { writer ->
|
||||
transformer.transform(DOMSource(this), StreamResult(writer))
|
||||
}
|
||||
} else {
|
||||
transformer.transform(DOMSource(this), StreamResult(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val readerCount = mutableMapOf<File, Int>()
|
||||
private val isAndroid = System.getProperty("java.runtime.name") == "Android Runtime"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ internal object PatchLoaderTest {
|
||||
patchLoaderCompanionObject,
|
||||
TEST_PATCHES_CLASS_LOADER,
|
||||
mapOf(File("patchesFile") to setOf(TEST_PATCHES_CLASS)),
|
||||
null
|
||||
).values.first()
|
||||
|
||||
assertEquals(
|
||||
|
||||
Reference in New Issue
Block a user