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: jobs:
release: release:
name: Release name: Release
permissions:
contents: write
packages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@ -46,5 +49,5 @@ jobs:
- name: Release - name: Release
env: env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm exec semantic-release run: npm exec semantic-release

View File

@ -23,7 +23,8 @@
"assets": [ "assets": [
"CHANGELOG.md", "CHANGELOG.md",
"gradle.properties" "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) # [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") @Surpress("unused")
val resourcePatch = rawResourcePatch { val resourcePatch = resourcePatch {
execute { execute {
// TODO // TODO
} }

View File

@ -1,3 +1,3 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME 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 # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -83,7 +85,9 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} 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. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # 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 ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # 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" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac 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. # 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"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command; # Collect all arguments for the java command:
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# shell script including quotes and variable substitutions, so put them in # and any embedded shellness will be escaped.
# double quotes to make sure that they get re-expanded; and # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# * put everything else in single quotes, so that it's not re-expanded. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-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 See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail 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", "@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1", "gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^23.0.2" "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? // 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 (strings?.firstNotNullOfOrNull { lookupMaps.methodsByStrings[it] }?.let(::match) == true) {
if (lookupByStrings()?.let(::match) == true) {
return true return true
} }
// No strings declared or none matched (partial matches are allowed). context.classes.forEach { classDef ->
// Use signature matching. if (match(context, classDef)) return true
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 = return false
buildString {
append(accessFlags)
append(returnTypeValue.first())
appendParameters(parameters ?: return@buildString)
}
return lookupMaps.methodsBySignature[signature] ?: return MethodClassPairs()
}
return match(lookupBySignature())
} }
/** /**
@ -128,7 +107,7 @@ class Fingerprint internal constructor(
fun match( fun match(
context: BytecodePatchContext, context: BytecodePatchContext,
method: Method, method: Method,
) = match(context, method, context.classByType(method.definingClass)!!.immutableClass) ) = match(context, method, context.classBy { method.definingClass == it.type }!!.immutableClass)
/** /**
* Match using a [Method]. * Match using a [Method].

View File

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

View File

@ -20,6 +20,7 @@ class PatcherConfig(
private val temporaryFilesPath: File = File("revanced-temporary-files"), private val temporaryFilesPath: File = File("revanced-temporary-files"),
aaptBinaryPath: String? = null, aaptBinaryPath: String? = null,
frameworkFileDirectory: 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, internal val multithreadingDexFileWriter: Boolean = false,
) { ) {
private val logger = Logger.getLogger(PatcherConfig::class.java.name) private val logger = Logger.getLogger(PatcherConfig::class.java.name)

View File

@ -21,7 +21,6 @@ import lanchon.multidexlib2.MultiDexIO
import lanchon.multidexlib2.RawDexIO import lanchon.multidexlib2.RawDexIO
import java.io.Closeable import java.io.Closeable
import java.io.FileFilter import java.io.FileFilter
import java.io.InputStream
import java.util.* import java.util.*
import java.util.logging.Logger import java.util.logging.Logger
@ -60,19 +59,18 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
internal val lookupMaps by lazy { LookupMaps(classes) } 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 { 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) } classes.forEach { classDef -> put(classDef.type, classDef) }
} }
/** forEachRecursively { patch ->
* Merge an extension to [classes]. if (patch is BytecodePatch && patch.extension != null) {
*
* @param extensionInputStream The input stream of the extension to merge. val extension = patch.extension.readAllBytes()
*/
internal fun merge(extensionInputStream: InputStream) {
val extension = extensionInputStream.readAllBytes()
RawDexIO.readRawDexFile(extension, 0, null).classes.forEach { classDef -> RawDexIO.readRawDexFile(extension, 0, null).classes.forEach { classDef ->
val existingClass = classesByType[classDef.type] ?: run { val existingClass = classesByType[classDef.type] ?: run {
@ -97,6 +95,8 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
} }
} }
} }
}
}
/** /**
* Find a class by its type using a contains check. * Find a class by its type using a contains check.
@ -104,6 +104,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
* @param type The type of the class. * @param type The type of the class.
* @return A proxy for the first class that matches the type. * @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 } 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> { override fun get(): Set<PatcherResult.PatchedDexFile> {
logger.info("Compiling patched dex files") logger.info("Compiling patched dex files")
// Free up memory before compiling the dex files.
lookupMaps.close()
val patchedDexFileResults = val patchedDexFileResults =
config.patchedFiles.resolve("dex").also { config.patchedFiles.resolve("dex").also {
it.deleteRecursively() // Make sure the directory is empty. 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. * @param classes The list of classes to create the lookup maps from.
*/ */
internal class LookupMaps internal constructor(classes: List<ClassDef>) : Closeable { 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. * Methods associated by strings referenced in it.
*/ */
@ -202,22 +191,6 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
classDef.methods.forEach { method -> classDef.methods.forEach { method ->
val methodClassPair: MethodClassPair = method to classDef 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. // Add strings contained in the method as the key.
method.instructionsOrNull?.forEach instructions@{ instruction -> method.instructionsOrNull?.forEach instructions@{ instruction ->
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO) { 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() { override fun close() {
allMethods.clear()
methodsBySignature.clear()
methodsByStrings.clear() methodsByStrings.clear()
} }
} }
override fun close() { override fun close() {
lookupMaps.close() lookupMaps.close()
classesByType.clear()
classes.clear() classes.clear()
} }
} }

View File

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

View File

@ -10,6 +10,9 @@ import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.MultiDexIO import lanchon.multidexlib2.MultiDexIO
import java.io.File import java.io.File
import java.io.InputStream 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.net.URLClassLoader
import java.util.jar.JarFile import java.util.jar.JarFile
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -636,7 +639,7 @@ sealed class PatchLoader private constructor(
*/ */
private val Class<*>.patchFields private val Class<*>.patchFields
get() = fields.filter { field -> get() = fields.filter { field ->
field.type.isPatch && field.canAccess(null) field.type.isPatch && field.canAccess()
}.map { field -> }.map { field ->
field.get(null) as Patch<*> field.get(null) as Patch<*>
} }
@ -646,7 +649,7 @@ sealed class PatchLoader private constructor(
*/ */
private val Class<*>.patchMethods private val Class<*>.patchMethods
get() = methods.filter { method -> get() = methods.filter { method ->
method.returnType.isPatch && method.parameterCount == 0 && method.canAccess(null) method.returnType.isPatch && method.parameterCount == 0 && method.canAccess()
}.map { method -> }.map { method ->
method.invoke(null) as Patch<*> method.invoke(null) as Patch<*>
} }
@ -670,6 +673,12 @@ sealed class PatchLoader private constructor(
it.name != null it.name != null
}.toSet() }.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: MutableClass.() -> Unit,
) { ) {
callback(targetClass) callback(targetClass)
this.classByType(targetClass.superclass ?: return)?.mutableClass?.let {
targetClass.superclass ?: return
this.classBy { targetClass.superclass == it.type }?.mutableClass?.let {
traverseClassHierarchy(it, callback) 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.ImmutableClassDef
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import io.mockk.every import io.mockk.every
import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.runs
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
@ -193,6 +195,7 @@ internal object PatcherTest {
private operator fun Set<Patch<*>>.invoke(): List<PatchResult> { private operator fun Set<Patch<*>>.invoke(): List<PatchResult> {
every { patcher.context.executablePatches } returns toMutableSet() every { patcher.context.executablePatches } returns toMutableSet()
every { patcher.context.bytecodeContext.lookupMaps } returns LookupMaps(patcher.context.bytecodeContext.classes) 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() } return runBlocking { patcher().toList() }
} }