chore: Merge branch dev to main (#304)

This commit is contained in:
oSumAtrIX 2024-10-13 03:52:31 +02:00 committed by GitHub
commit b2aecb726d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1987 additions and 1257 deletions

View File

@ -10,6 +10,9 @@ on:
jobs:
release:
name: Release
permissions:
contents: write
packages: write
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -46,5 +49,5 @@ jobs:
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm exec semantic-release

View File

@ -23,7 +23,8 @@
"assets": [
"CHANGELOG.md",
"gradle.properties"
]
],
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
[

View File

@ -1,3 +1,33 @@
## [20.0.1-dev.5](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.4...v20.0.1-dev.5) (2024-10-11)
### Bug Fixes
* Use non-nullable type for options ([ea6fc70](https://github.com/ReVanced/revanced-patcher/commit/ea6fc70caab055251ad4d0d3f1b5cf53865abb85))
## [20.0.1-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.3...v20.0.1-dev.4) (2024-10-07)
### Bug Fixes
* Make it work on Android by not using APIs from JVM unavailable to Android. ([2be6e97](https://github.com/ReVanced/revanced-patcher/commit/2be6e97817437f40e17893dfff3bea2cd4c3ff9e))
## [20.0.1-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.2...v20.0.1-dev.3) (2024-10-03)
### Performance Improvements
* Free memory earlier and remove negligible lookup maps ([d53aacd](https://github.com/ReVanced/revanced-patcher/commit/d53aacdad4ed3750ddae526fb307577ea36e6171))
## [20.0.1-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.1...v20.0.1-dev.2) (2024-10-01)
## [20.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0...v20.0.1-dev.1) (2024-09-18)
### Bug Fixes
* Check for class type exactly instead of with contains ([#310](https://github.com/ReVanced/revanced-patcher/issues/310)) ([69f2f20](https://github.com/ReVanced/revanced-patcher/commit/69f2f20fd99162f91cd9c531dfe47d00d3152ead))
# [20.0.0](https://github.com/ReVanced/revanced-patcher/compare/v19.3.1...v20.0.0) (2024-08-06)

View File

@ -108,7 +108,7 @@ val rawResourcePatch = rawResourcePatch {
}
@Surpress("unused")
val resourcePatch = rawResourcePatch {
val resourcePatch = resourcePatch {
execute {
// TODO
}

View File

@ -1,3 +1,3 @@
org.gradle.parallel = true
org.gradle.caching = true
version = 20.0.0
version = 20.0.1-dev.5

Binary file not shown.

View File

@ -1,6 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dist
zipStorePath=wrapper/dists

22
gradlew vendored
View File

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -83,7 +85,9 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -201,11 +205,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \

22
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail

2972
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1",
"semantic-release": "^23.0.2"
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2"
}
}

View File

@ -66,36 +66,15 @@ class Fingerprint internal constructor(
}
// TODO: If only one string is necessary, why not use a single string for every fingerprint?
fun Fingerprint.lookupByStrings() = strings?.firstNotNullOfOrNull { lookupMaps.methodsByStrings[it] }
if (lookupByStrings()?.let(::match) == true) {
if (strings?.firstNotNullOfOrNull { lookupMaps.methodsByStrings[it] }?.let(::match) == true) {
return true
}
// No strings declared or none matched (partial matches are allowed).
// Use signature matching.
fun Fingerprint.lookupBySignature(): MethodClassPairs {
if (accessFlags == null) return lookupMaps.allMethods
var returnTypeValue = returnType
if (returnTypeValue == null) {
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
// Constructors always have void return type.
returnTypeValue = "V"
} else {
return lookupMaps.allMethods
}
}
val signature =
buildString {
append(accessFlags)
append(returnTypeValue.first())
appendParameters(parameters ?: return@buildString)
}
return lookupMaps.methodsBySignature[signature] ?: return MethodClassPairs()
context.classes.forEach { classDef ->
if (match(context, classDef)) return true
}
return match(lookupBySignature())
return false
}
/**
@ -128,7 +107,7 @@ class Fingerprint internal constructor(
fun match(
context: BytecodePatchContext,
method: Method,
) = match(context, method, context.classByType(method.definingClass)!!.immutableClass)
) = match(context, method, context.classBy { method.definingClass == it.type }!!.immutableClass)
/**
* Match using a [Method].

View File

@ -98,14 +98,12 @@ class Patcher(private val config: PatcherConfig) : Closeable {
logger.info("Merging extensions")
context.executablePatches.forEachRecursively { patch ->
if (patch is BytecodePatch && patch.extension != null) {
context.bytecodeContext.merge(patch.extension)
}
}
with(context.bytecodeContext) {
context.executablePatches.mergeExtensions()
// Initialize lookup maps.
context.bytecodeContext.lookupMaps
// Initialize lookup maps.
lookupMaps
}
logger.info("Executing patches")

View File

@ -20,6 +20,7 @@ class PatcherConfig(
private val temporaryFilesPath: File = File("revanced-temporary-files"),
aaptBinaryPath: String? = null,
frameworkFileDirectory: String? = null,
@Deprecated("This is going to be removed in the future because it is not needed anymore.")
internal val multithreadingDexFileWriter: Boolean = false,
) {
private val logger = Logger.getLogger(PatcherConfig::class.java.name)

View File

@ -21,7 +21,6 @@ import lanchon.multidexlib2.MultiDexIO
import lanchon.multidexlib2.RawDexIO
import java.io.Closeable
import java.io.FileFilter
import java.io.InputStream
import java.util.*
import java.util.logging.Logger
@ -60,40 +59,41 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
internal val lookupMaps by lazy { LookupMaps(classes) }
/**
* A map for lookup by [merge].
* Merge the extensions for this set of patches.
*/
internal val classesByType = mutableMapOf<String, ClassDef>().apply {
classes.forEach { classDef -> put(classDef.type, classDef) }
}
internal fun Set<Patch<*>>.mergeExtensions() {
// Lookup map for fast checking if a class exists by its type.
val classesByType = mutableMapOf<String, ClassDef>().apply {
classes.forEach { classDef -> put(classDef.type, classDef) }
}
/**
* Merge an extension to [classes].
*
* @param extensionInputStream The input stream of the extension to merge.
*/
internal fun merge(extensionInputStream: InputStream) {
val extension = extensionInputStream.readAllBytes()
forEachRecursively { patch ->
if (patch is BytecodePatch && patch.extension != null) {
RawDexIO.readRawDexFile(extension, 0, null).classes.forEach { classDef ->
val existingClass = classesByType[classDef.type] ?: run {
logger.fine("Adding class \"$classDef\"")
val extension = patch.extension.readAllBytes()
classes += classDef
classesByType[classDef.type] = classDef
RawDexIO.readRawDexFile(extension, 0, null).classes.forEach { classDef ->
val existingClass = classesByType[classDef.type] ?: run {
logger.fine("Adding class \"$classDef\"")
return@forEach
}
classes += classDef
classesByType[classDef.type] = classDef
logger.fine("Class \"$classDef\" exists already. Adding missing methods and fields.")
return@forEach
}
existingClass.merge(classDef, this@BytecodePatchContext).let { mergedClass ->
// If the class was merged, replace the original class with the merged class.
if (mergedClass === existingClass) {
return@let
logger.fine("Class \"$classDef\" exists already. Adding missing methods and fields.")
existingClass.merge(classDef, this@BytecodePatchContext).let { mergedClass ->
// If the class was merged, replace the original class with the merged class.
if (mergedClass === existingClass) {
return@let
}
classes -= existingClass
classes += mergedClass
}
}
classes -= existingClass
classes += mergedClass
}
}
}
@ -104,6 +104,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
* @param type The type of the class.
* @return A proxy for the first class that matches the type.
*/
@Deprecated("Use classBy { type in it.type } instead.", ReplaceWith("classBy { type in it.type }"))
fun classByType(type: String) = classBy { type in it.type }
/**
@ -144,6 +145,9 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
override fun get(): Set<PatcherResult.PatchedDexFile> {
logger.info("Compiling patched dex files")
// Free up memory before compiling the dex files.
lookupMaps.close()
val patchedDexFileResults =
config.patchedFiles.resolve("dex").also {
it.deleteRecursively() // Make sure the directory is empty.
@ -177,21 +181,6 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
* @param classes The list of classes to create the lookup maps from.
*/
internal class LookupMaps internal constructor(classes: List<ClassDef>) : Closeable {
/**
* Classes associated by their type.
*/
internal val classesByType = classes.associateBy { it.type }.toMutableMap()
/**
* All methods and the class they are a member of.
*/
internal val allMethods = MethodClassPairs()
/**
* Methods associated by its access flags, return type and parameter.
*/
internal val methodsBySignature = MethodClassPairsLookupMap()
/**
* Methods associated by strings referenced in it.
*/
@ -202,22 +191,6 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
classDef.methods.forEach { method ->
val methodClassPair: MethodClassPair = method to classDef
// For fingerprints with no access or return type specified.
allMethods += methodClassPair
val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first()
// Add <access><returnType> as the key.
methodsBySignature[accessFlagsReturnKey] = methodClassPair
// Add <access><returnType>[parameters] as the key.
methodsBySignature[
buildString {
append(accessFlagsReturnKey)
appendParameters(method.parameterTypes)
},
] = methodClassPair
// Add strings contained in the method as the key.
method.instructionsOrNull?.forEach instructions@{ instruction ->
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO) {
@ -258,15 +231,12 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
}
override fun close() {
allMethods.clear()
methodsBySignature.clear()
methodsByStrings.clear()
}
}
override fun close() {
lookupMaps.close()
classesByType.clear()
classes.clear()
}
}

View File

@ -231,7 +231,7 @@ fun intOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Int?>.(Int?) -> Boolean = { true },
validator: Option<Int>.(Int?) -> Boolean = { true },
) = option(
key,
default,
@ -264,7 +264,7 @@ fun PatchBuilder<*>.intOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Int?>.(Int?) -> Boolean = { true },
validator: Option<Int>.(Int?) -> Boolean = { true },
) = option(
key,
default,
@ -297,7 +297,7 @@ fun booleanOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Boolean?>.(Boolean?) -> Boolean = { true },
validator: Option<Boolean>.(Boolean?) -> Boolean = { true },
) = option(
key,
default,
@ -330,7 +330,7 @@ fun PatchBuilder<*>.booleanOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Boolean?>.(Boolean?) -> Boolean = { true },
validator: Option<Boolean>.(Boolean?) -> Boolean = { true },
) = option(
key,
default,
@ -363,7 +363,7 @@ fun floatOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Float?>.(Float?) -> Boolean = { true },
validator: Option<Float>.(Float?) -> Boolean = { true },
) = option(
key,
default,
@ -396,7 +396,7 @@ fun PatchBuilder<*>.floatOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Float?>.(Float?) -> Boolean = { true },
validator: Option<Float>.(Float?) -> Boolean = { true },
) = option(
key,
default,
@ -429,7 +429,7 @@ fun longOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Long?>.(Long?) -> Boolean = { true },
validator: Option<Long>.(Long?) -> Boolean = { true },
) = option(
key,
default,
@ -462,7 +462,7 @@ fun PatchBuilder<*>.longOption(
title: String? = null,
description: String? = null,
required: Boolean = false,
validator: Option<Long?>.(Long?) -> Boolean = { true },
validator: Option<Long>.(Long?) -> Boolean = { true },
) = option(
key,
default,

View File

@ -10,6 +10,9 @@ import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.MultiDexIO
import java.io.File
import java.io.InputStream
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.util.jar.JarFile
import kotlin.reflect.KProperty
@ -636,7 +639,7 @@ sealed class PatchLoader private constructor(
*/
private val Class<*>.patchFields
get() = fields.filter { field ->
field.type.isPatch && field.canAccess(null)
field.type.isPatch && field.canAccess()
}.map { field ->
field.get(null) as Patch<*>
}
@ -646,7 +649,7 @@ sealed class PatchLoader private constructor(
*/
private val Class<*>.patchMethods
get() = methods.filter { method ->
method.returnType.isPatch && method.parameterCount == 0 && method.canAccess(null)
method.returnType.isPatch && method.parameterCount == 0 && method.canAccess()
}.map { method ->
method.invoke(null) as Patch<*>
}
@ -670,6 +673,12 @@ sealed class PatchLoader private constructor(
it.name != null
}.toSet()
}
private fun Member.canAccess(): Boolean {
if (this is Method && parameterCount != 0) return false
return Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)
}
}
}

View File

@ -179,7 +179,9 @@ internal object ClassMerger {
callback: MutableClass.() -> Unit,
) {
callback(targetClass)
this.classByType(targetClass.superclass ?: return)?.mutableClass?.let {
targetClass.superclass ?: return
this.classBy { targetClass.superclass == it.type }?.mutableClass?.let {
traverseClassHierarchy(it, callback)
}
}

View File

@ -6,7 +6,9 @@ import app.revanced.patcher.util.ProxyClassList
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.BeforeEach
@ -193,6 +195,7 @@ internal object PatcherTest {
private operator fun Set<Patch<*>>.invoke(): List<PatchResult> {
every { patcher.context.executablePatches } returns toMutableSet()
every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classes)
every { with(patcher.context.bytecodeContext) { any<Set<Patch<*>>>().mergeExtensions() } } just runs
return runBlocking { patcher().toList() }
}