You've already forked revanced-patcher
							
							
				mirror of
				https://github.com/revanced/revanced-patcher
				synced 2025-11-02 07:30:52 +01:00 
			
		
		
		
	Compare commits
	
		
			18 Commits
		
	
	
		
			v20.0.1-de
			...
			fix/merge-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1f0eab36d9 | ||
| 
						 | 
					7be0cd8548 | ||
| 
						 | 
					ab624f04f6 | ||
| 
						 | 
					21b5c079fb | ||
| 
						 | 
					5024204046 | ||
| 
						 | 
					a44802ef4e | ||
| 
						 | 
					4c1c34ad01 | ||
| 
						 | 
					b2aecb726d | ||
| 
						 | 
					851f9c7885 | ||
| 
						 | 
					ea6fc70caa | ||
| 
						 | 
					a2875d1d64 | ||
| 
						 | 
					2be6e97817 | ||
| 
						 | 
					348d0070e7 | ||
| 
						 | 
					d53aacdad4 | ||
| 
						 | 
					f1615b7ab5 | ||
| 
						 | 
					ffb1d880d7 | ||
| 
						 | 
					e95f13ae3e | ||
| 
						 | 
					e1b984d601 | 
							
								
								
									
										5
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,8 @@
 | 
			
		||||
        "assets": [
 | 
			
		||||
          "CHANGELOG.md",
 | 
			
		||||
          "gradle.properties"
 | 
			
		||||
        ]
 | 
			
		||||
        ],
 | 
			
		||||
        "message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,54 @@
 | 
			
		||||
## [20.0.2](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1...v20.0.2) (2024-10-17)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
* Make it work on Android 12 and lower by using existing APIs ([#312](https://github.com/ReVanced/revanced-patcher/issues/312)) ([a44802e](https://github.com/ReVanced/revanced-patcher/commit/a44802ef4ebf59ae47213854ba761c81dadc51f3))
 | 
			
		||||
 | 
			
		||||
## [20.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1...v20.0.2-dev.1) (2024-10-15)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
* Make it work on Android 12 and lower by using existing APIs ([#312](https://github.com/ReVanced/revanced-patcher/issues/312)) ([a44802e](https://github.com/ReVanced/revanced-patcher/commit/a44802ef4ebf59ae47213854ba761c81dadc51f3))
 | 
			
		||||
 | 
			
		||||
## [20.0.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0...v20.0.1) (2024-10-13)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 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))
 | 
			
		||||
* Make it work on Android by not using APIs from JVM unavailable to Android. ([2be6e97](https://github.com/ReVanced/revanced-patcher/commit/2be6e97817437f40e17893dfff3bea2cd4c3ff9e))
 | 
			
		||||
* Use non-nullable type for options ([ea6fc70](https://github.com/ReVanced/revanced-patcher/commit/ea6fc70caab055251ad4d0d3f1b5cf53865abb85))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Performance Improvements
 | 
			
		||||
 | 
			
		||||
* Free memory earlier and remove negligible lookup maps ([d53aacd](https://github.com/ReVanced/revanced-patcher/commit/d53aacdad4ed3750ddae526fb307577ea36e6171))
 | 
			
		||||
 | 
			
		||||
## [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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
org.gradle.parallel = true
 | 
			
		||||
org.gradle.caching = true
 | 
			
		||||
version = 20.0.1-dev.1
 | 
			
		||||
version = 20.0.2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2972
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2972
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -91,20 +91,14 @@ class Patcher(private val config: PatcherConfig) : Closeable {
 | 
			
		||||
            }.also { executedPatches[this] = it }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Prevent from decoding the app manifest twice if it is not needed.
 | 
			
		||||
        // Prevent decoding the app manifest twice if it is not needed.
 | 
			
		||||
        if (config.resourceMode != ResourcePatchContext.ResourceMode.NONE) {
 | 
			
		||||
            context.resourceContext.decodeResources(config.resourceMode)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.info("Merging extensions")
 | 
			
		||||
        logger.info("Initializing lookup maps")
 | 
			
		||||
 | 
			
		||||
        context.executablePatches.forEachRecursively { patch ->
 | 
			
		||||
            if (patch is BytecodePatch && patch.extension != null) {
 | 
			
		||||
                context.bytecodeContext.merge(patch.extension)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Initialize lookup maps.
 | 
			
		||||
        // Accessing the lazy lookup maps to initialize them.
 | 
			
		||||
        context.bytecodeContext.lookupMaps
 | 
			
		||||
 | 
			
		||||
        logger.info("Executing patches")
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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,42 +59,33 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
 | 
			
		||||
    internal val lookupMaps by lazy { LookupMaps(classes) }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A map for lookup by [merge].
 | 
			
		||||
     * Merge the extension of this patch.
 | 
			
		||||
     */
 | 
			
		||||
    internal val classesByType = mutableMapOf<String, ClassDef>().apply {
 | 
			
		||||
        classes.forEach { classDef -> put(classDef.type, classDef) }
 | 
			
		||||
    }
 | 
			
		||||
    internal fun BytecodePatch.mergeExtension() {
 | 
			
		||||
        extension?.use { extensionStream ->
 | 
			
		||||
            RawDexIO.readRawDexFile(extensionStream, 0, null).classes.forEach { classDef ->
 | 
			
		||||
                val existingClass = lookupMaps.classesByType[classDef.type] ?: run {
 | 
			
		||||
                    logger.fine("Adding class \"$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()
 | 
			
		||||
                    classes += classDef
 | 
			
		||||
                    lookupMaps.classesByType[classDef.type] = classDef
 | 
			
		||||
 | 
			
		||||
        RawDexIO.readRawDexFile(extension, 0, null).classes.forEach { classDef ->
 | 
			
		||||
            val existingClass = classesByType[classDef.type] ?: run {
 | 
			
		||||
                logger.fine("Adding class \"$classDef\"")
 | 
			
		||||
 | 
			
		||||
                classes += classDef
 | 
			
		||||
                classesByType[classDef.type] = classDef
 | 
			
		||||
 | 
			
		||||
                return@forEach
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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
 | 
			
		||||
                    return@forEach
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                classes -= existingClass
 | 
			
		||||
                classes += mergedClass
 | 
			
		||||
                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
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        } ?: return logger.fine("No extension to merge")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -145,6 +135,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.
 | 
			
		||||
@@ -178,47 +171,21 @@ 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.
 | 
			
		||||
         */
 | 
			
		||||
        internal val methodsByStrings = MethodClassPairsLookupMap()
 | 
			
		||||
 | 
			
		||||
        // 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) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            classes.forEach { classDef ->
 | 
			
		||||
                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) {
 | 
			
		||||
@@ -259,15 +226,13 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun close() {
 | 
			
		||||
            allMethods.clear()
 | 
			
		||||
            methodsBySignature.clear()
 | 
			
		||||
            methodsByStrings.clear()
 | 
			
		||||
            classesByType.clear()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun close() {
 | 
			
		||||
        lookupMaps.close()
 | 
			
		||||
        classesByType.clear()
 | 
			
		||||
        classes.clear()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -155,7 +158,13 @@ class BytecodePatch internal constructor(
 | 
			
		||||
    finalizeBlock,
 | 
			
		||||
) {
 | 
			
		||||
    override fun execute(context: PatcherContext) = with(context.bytecodeContext) {
 | 
			
		||||
        fingerprints.forEach { it.match(this) }
 | 
			
		||||
        with(context.bytecodeContext) {
 | 
			
		||||
            mergeExtension()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fingerprints.forEach {
 | 
			
		||||
            it.match(this)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        execute(this)
 | 
			
		||||
    }
 | 
			
		||||
@@ -636,7 +645,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 +655,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 +679,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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<BytecodePatch>().mergeExtension() } } just runs
 | 
			
		||||
 | 
			
		||||
        return runBlocking { patcher().toList() }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user