diff --git a/build.gradle.kts b/build.gradle.kts index aa564fe..ce99a05 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,4 +83,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/IntegrationsConsumer.kt b/src/main/kotlin/app/revanced/patcher/IntegrationsConsumer.kt index 27ab7b9..4a2d211 100644 --- a/src/main/kotlin/app/revanced/patcher/IntegrationsConsumer.kt +++ b/src/main/kotlin/app/revanced/patcher/IntegrationsConsumer.kt @@ -5,4 +5,4 @@ import java.io.File @FunctionalInterface interface IntegrationsConsumer { fun acceptIntegrations(integrations: List) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt b/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt index 825bafc..7d3075f 100644 --- a/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt +++ b/src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt @@ -37,7 +37,7 @@ sealed class PatchBundleLoader private constructor( // This constructor parameter is unfortunately necessary, // so that a reference to the mutable set is present in the constructor to be able to add patches to it. // because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter. - private val patchSet: MutableSet> = mutableSetOf() + private val patchSet: MutableSet> = mutableSetOf(), ) : PatchSet by patchSet { private val logger = Logger.getLogger(PatchBundleLoader::class.java.name) @@ -63,22 +63,29 @@ sealed class PatchBundleLoader private constructor( * @param silent Whether to suppress logging. * @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated. */ - internal fun Class<*>.getInstance(logger: Logger, silent: Boolean = false): Patch<*>? { + internal fun Class<*>.getInstance( + logger: Logger, + silent: Boolean = false, + ): Patch<*>? { return try { getField("INSTANCE").get(null) } catch (exception: NoSuchFieldException) { - if (!silent) logger.fine( - "Patch class '${name}' has no INSTANCE field, therefor not a singleton. " + - "Will try to instantiate it." - ) + if (!silent) { + logger.fine( + "Patch class '$name' has no INSTANCE field, therefor not a singleton. " + + "Will try to instantiate it.", + ) + } try { getDeclaredConstructor().newInstance() } catch (exception: Exception) { - if (!silent) logger.severe( - "Patch class '${name}' is not singleton and has no suitable constructor, " + - "therefor cannot be instantiated and will be ignored." - ) + if (!silent) { + logger.severe( + "Patch class '$name' is not singleton and has no suitable constructor, " + + "therefor cannot be instantiated and will be ignored.", + ) + } return null } @@ -97,7 +104,7 @@ sealed class PatchBundleLoader private constructor( { patchBundle -> JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") } .map { it.name.replace('/', '.').replace(".class", "") } - } + }, ) /** @@ -109,9 +116,10 @@ sealed class PatchBundleLoader private constructor( */ class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader( DexClassLoader( - patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, optimizedDexDirectory?.absolutePath, + patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, + optimizedDexDirectory?.absolutePath, null, - PatchBundleLoader::class.java.classLoader + PatchBundleLoader::class.java.classLoader, ), patchBundles, { patchBundle -> @@ -119,9 +127,9 @@ sealed class PatchBundleLoader private constructor( .map { classDef -> classDef.type.substring(1, classDef.length - 1) } - } + }, ) { @Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.") constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/PatchExecutorFunction.kt b/src/main/kotlin/app/revanced/patcher/PatchExecutorFunction.kt index 6ef5c12..da5aaa6 100644 --- a/src/main/kotlin/app/revanced/patcher/PatchExecutorFunction.kt +++ b/src/main/kotlin/app/revanced/patcher/PatchExecutorFunction.kt @@ -5,4 +5,4 @@ import kotlinx.coroutines.flow.Flow import java.util.function.Function @FunctionalInterface -interface PatchExecutorFunction : Function> \ No newline at end of file +interface PatchExecutorFunction : Function> diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 39ee730..2c9f67c 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -17,7 +17,7 @@ import java.util.logging.Logger * @param options The options for the patcher. */ class Patcher( - private val options: PatcherOptions + private val options: PatcherOptions, ) : PatchExecutorFunction, PatchesConsumer, IntegrationsConsumer, Supplier, Closeable { private val logger = Logger.getLogger(Patcher::class.java.name) @@ -39,11 +39,12 @@ class Patcher( // * @param patches The [Patch]es to add. // * @throws PatcherException.CircularDependencyException If a circular dependency is detected. // */ + /** * Add [Patch]es to ReVanced [Patcher]. * * @param patches The [Patch]es to add. - */ + */ @Suppress("NAME_SHADOWING") override fun acceptPatches(patches: List>) { /** @@ -82,7 +83,7 @@ class Patcher( dependency.visit() } } - */ + */ /** * Returns true if at least one patch or its dependencies matches the given predicate. @@ -126,128 +127,130 @@ class Patcher( * @param returnOnError If true, ReVanced [Patcher] will return immediately if a [Patch] fails. * @return A pair of the name of the [Patch] and its [PatchResult]. */ - override fun apply(returnOnError: Boolean) = flow { + override fun apply(returnOnError: Boolean) = + flow { + /** + * Execute a [Patch] and its dependencies recursively. + * + * @param patch The [Patch] to execute. + * @param executedPatches A map to prevent [Patch]es from being executed twice due to dependencies. + * @return The result of executing the [Patch]. + */ + fun executePatch( + patch: Patch<*>, + executedPatches: LinkedHashMap, PatchResult>, + ): PatchResult { + val patchName = patch.name ?: patch.toString() - /** - * Execute a [Patch] and its dependencies recursively. - * - * @param patch The [Patch] to execute. - * @param executedPatches A map to prevent [Patch]es from being executed twice due to dependencies. - * @return The result of executing the [Patch]. - */ - fun executePatch( - patch: Patch<*>, - executedPatches: LinkedHashMap, PatchResult> - ): PatchResult { - val patchName = patch.name ?: patch.toString() + executedPatches[patch]?.let { patchResult -> + patchResult.exception ?: return patchResult - executedPatches[patch]?.let { patchResult -> - patchResult.exception ?: return patchResult + // Return a new result with an exception indicating that the patch was not executed previously, + // because it is a dependency of another patch that failed. + return PatchResult(patch, PatchException("'$patchName' did not succeed previously")) + } - // Return a new result with an exception indicating that the patch was not executed previously, - // because it is a dependency of another patch that failed. - return PatchResult(patch, PatchException("'$patchName' did not succeed previously")) - } + // Recursively execute all dependency patches. + patch.dependencies?.forEach { dependencyClass -> + val dependency = context.allPatches[dependencyClass]!! + val result = executePatch(dependency, executedPatches) - // Recursively execute all dependency patches. - patch.dependencies?.forEach { dependencyClass -> - val dependency = context.allPatches[dependencyClass]!! - val result = executePatch(dependency, executedPatches) - - result.exception?.let { - return PatchResult( - patch, - PatchException( - "'$patchName' depends on '${dependency.name ?: dependency}' " + - "that raised an exception:\n${it.stackTraceToString()}" + result.exception?.let { + return PatchResult( + patch, + PatchException( + "'$patchName' depends on '${dependency.name ?: dependency}' " + + "that raised an exception:\n${it.stackTraceToString()}", + ), ) - ) - } - } - - return try { - // TODO: Implement this in a more polymorphic way. - when (patch) { - is BytecodePatch -> { - patch.fingerprints.resolveUsingLookupMap(context.bytecodeContext) - patch.execute(context.bytecodeContext) - } - is ResourcePatch -> { - patch.execute(context.resourceContext) } } - PatchResult(patch) - } catch (exception: PatchException) { - PatchResult(patch, exception) - } catch (exception: Exception) { - PatchResult(patch, PatchException(exception)) - }.also { executedPatches[patch] = it } - } + return try { + // TODO: Implement this in a more polymorphic way. + when (patch) { + is BytecodePatch -> { + patch.fingerprints.resolveUsingLookupMap(context.bytecodeContext) + patch.execute(context.bytecodeContext) + } + is ResourcePatch -> { + patch.execute(context.resourceContext) + } + } - if (context.bytecodeContext.integrations.merge) context.bytecodeContext.integrations.flush() - - LookupMap.initializeLookupMaps(context.bytecodeContext) - - // Prevent from decoding the app manifest twice if it is not needed. - if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL) - context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL) - - logger.info("Executing patches") - - val executedPatches = LinkedHashMap, PatchResult>() // Key is name. - - context.executablePatches.values.sortedBy { it.name }.forEach { patch -> - val patchResult = executePatch(patch, executedPatches) - - // If the patch failed, emit the result, even if it is closeable. - // Results of executed patches that are closeable will be emitted later. - patchResult.exception?.let { - // Propagate exception to caller instead of wrapping it in a new exception. - emit(patchResult) - - if (returnOnError) return@flow - } ?: run { - if (patch is Closeable) return@run - - emit(patchResult) - } - } - - executedPatches.values - .filter { it.exception == null } - .filter { it.patch is Closeable }.asReversed().forEach { executedPatch -> - val patch = executedPatch.patch - - val result = try { - (patch as Closeable).close() - - executedPatch + PatchResult(patch) } catch (exception: PatchException) { PatchResult(patch, exception) } catch (exception: Exception) { PatchResult(patch, PatchException(exception)) - } + }.also { executedPatches[patch] = it } + } - result.exception?.let { - emit( - PatchResult( - patch, - PatchException( - "'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}", - result.exception - ) - ) - ) + if (context.bytecodeContext.integrations.merge) context.bytecodeContext.integrations.flush() + + LookupMap.initializeLookupMaps(context.bytecodeContext) + + // Prevent from decoding the app manifest twice if it is not needed. + if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL) { + context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL) + } + + logger.info("Executing patches") + + val executedPatches = LinkedHashMap, PatchResult>() // Key is name. + + context.executablePatches.values.sortedBy { it.name }.forEach { patch -> + val patchResult = executePatch(patch, executedPatches) + + // If the patch failed, emit the result, even if it is closeable. + // Results of executed patches that are closeable will be emitted later. + patchResult.exception?.let { + // Propagate exception to caller instead of wrapping it in a new exception. + emit(patchResult) if (returnOnError) return@flow } ?: run { - patch.name ?: return@run + if (patch is Closeable) return@run - emit(result) + emit(patchResult) } } - } + + executedPatches.values + .filter { it.exception == null } + .filter { it.patch is Closeable }.asReversed().forEach { executedPatch -> + val patch = executedPatch.patch + + val result = + try { + (patch as Closeable).close() + + executedPatch + } catch (exception: PatchException) { + PatchResult(patch, exception) + } catch (exception: Exception) { + PatchResult(patch, PatchException(exception)) + } + + result.exception?.let { + emit( + PatchResult( + patch, + PatchException( + "'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}", + result.exception, + ), + ), + ) + + if (returnOnError) return@flow + } ?: run { + patch.name ?: return@run + + emit(result) + } + } + } override fun close() = LookupMap.clearLookupMaps() @@ -256,10 +259,10 @@ class Patcher( * * @return The [PatcherResult] containing the patched input files. */ - override fun get() = PatcherResult( - context.bytecodeContext.get(), - context.resourceContext.get(), - context.packageMetadata.apkInfo.doNotCompress?.toList() - ) + override fun get() = + PatcherResult( + context.bytecodeContext.get(), + context.resourceContext.get(), + context.packageMetadata.apkInfo.doNotCompress?.toList(), + ) } - diff --git a/src/main/kotlin/app/revanced/patcher/PatcherContext.kt b/src/main/kotlin/app/revanced/patcher/PatcherContext.kt index fffba74..03bdaf6 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherContext.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherContext.kt @@ -38,4 +38,4 @@ class PatcherContext internal constructor(options: PatcherOptions) { * This holds the current state of the bytecode. */ internal val bytecodeContext = BytecodeContext(options) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/PatcherException.kt b/src/main/kotlin/app/revanced/patcher/PatcherException.kt index 2313a99..9f92a01 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherException.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherException.kt @@ -9,8 +9,7 @@ package app.revanced.patcher sealed class PatcherException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) { constructor(errorMessage: String) : this(errorMessage, null) - class CircularDependencyException internal constructor(dependant: String) : PatcherException( - "Patch '$dependant' causes a circular dependency" + "Patch '$dependant' causes a circular dependency", ) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt index e5eb7b6..9c39557 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherOptions.kt @@ -32,20 +32,23 @@ data class PatcherOptions( /** * The configuration to use for resource decoding and compiling. */ - internal val resourceConfig = Config.getDefaultConfig().apply { - useAapt2 = true - aaptPath = aaptBinaryPath ?: "" - frameworkDirectory = frameworkFileDirectory - } - - fun recreateResourceCacheDirectory() = resourceCachePath.also { - if (it.exists()) { - logger.info("Deleting existing resource cache directory") - - if (!it.deleteRecursively()) - logger.severe("Failed to delete existing resource cache directory") + internal val resourceConfig = + Config.getDefaultConfig().apply { + useAapt2 = true + aaptPath = aaptBinaryPath ?: "" + frameworkDirectory = frameworkFileDirectory } - it.mkdirs() - } + fun recreateResourceCacheDirectory() = + resourceCachePath.also { + if (it.exists()) { + logger.info("Deleting existing resource cache directory") + + if (!it.deleteRecursively()) { + logger.severe("Failed to delete existing resource cache directory") + } + } + + it.mkdirs() + } } diff --git a/src/main/kotlin/app/revanced/patcher/PatcherResult.kt b/src/main/kotlin/app/revanced/patcher/PatcherResult.kt index da051a8..0df8634 100644 --- a/src/main/kotlin/app/revanced/patcher/PatcherResult.kt +++ b/src/main/kotlin/app/revanced/patcher/PatcherResult.kt @@ -12,7 +12,7 @@ import java.io.InputStream data class PatcherResult( val dexFiles: List, val resourceFile: File?, - val doNotCompress: List? = null + val doNotCompress: List? = null, ) { /** * Wrapper for dex files. @@ -20,4 +20,4 @@ data class PatcherResult( * @param stream The dex file as [InputStream]. */ class PatchedDexFile(val name: String, val stream: InputStream) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt b/src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt index 866b7e5..91b83b9 100644 --- a/src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt +++ b/src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt @@ -5,4 +5,4 @@ import app.revanced.patcher.patch.Patch @FunctionalInterface interface PatchesConsumer { fun acceptPatches(patches: List>) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt b/src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt index df06bc5..5629346 100644 --- a/src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt +++ b/src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt @@ -28,141 +28,153 @@ import java.util.logging.Logger */ class BytecodeContext internal constructor(private val options: PatcherOptions) : Context> { - private val logger = Logger.getLogger(BytecodeContext::class.java.name) - - /** - * [Opcodes] of the supplied [PatcherOptions.inputFile]. - */ - internal lateinit var opcodes: Opcodes - - /** - * The list of classes. - */ - val classes by lazy { - ProxyClassList( - MultiDexIO.readDexFile( - true, options.inputFile, BasicDexFileNamer(), null, null - ).also { opcodes = it.opcodes }.classes.toMutableSet() - ) - } - - /** - * The [Integrations] of this [PatcherContext]. - */ - internal val integrations = Integrations() - - /** - * Find a class by a given class name. - * - * @param className The name of the class. - * @return A proxy for the first class that matches the class name. - */ - fun findClass(className: String) = findClass { it.type.contains(className) } - - /** - * Find a class by a given predicate. - * - * @param predicate A predicate to match the class. - * @return A proxy for the first class that matches the predicate. - */ - fun findClass(predicate: (ClassDef) -> Boolean) = - // if we already proxied the class matching the predicate... - classes.proxies.firstOrNull { predicate(it.immutableClass) } ?: - // else resolve the class to a proxy and return it, if the predicate is matching a class - classes.find(predicate)?.let { proxy(it) } - - /** - * Proxy a class. - * This will allow the class to be modified. - * - * @param classDef The class to proxy. - * @return A proxy for the class. - */ - fun proxy(classDef: ClassDef) = this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let { - ClassProxy(classDef).also { this.classes.add(it) } - } - - /** - * Create a [MethodWalker] instance for the current [BytecodeContext]. - * - * @param startMethod The method to start at. - * @return A [MethodWalker] instance. - */ - fun toMethodWalker(startMethod: Method) = MethodWalker(this, startMethod) - - /** - * Compile bytecode from the [BytecodeContext]. - * - * @return The compiled bytecode. - */ - override fun get(): List { - logger.info("Compiling patched dex files") - - val patchedDexFileResults = options.resourceCachePath.resolve("dex").also { - it.deleteRecursively() // Make sure the directory is empty. - it.mkdirs() - }.apply { - MultiDexIO.writeDexFile( - true, - if (options.multithreadingDexFileWriter) -1 else 1, - this, - BasicDexFileNamer(), - object : DexFile { - override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses) - override fun getOpcodes() = this@BytecodeContext.opcodes - }, - DexIO.DEFAULT_MAX_DEX_POOL_SIZE - ) { _, entryName, _ -> logger.info("Compiled $entryName") } - }.listFiles(FileFilter { it.isFile })!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) } - - System.gc() - - return patchedDexFileResults - } - - /** - * The integrations of a [PatcherContext]. - */ - internal inner class Integrations : MutableList by mutableListOf(), Flushable { - /** - * Whether to merge integrations. - * Set to true, if the field requiresIntegrations of any supplied [Patch] is true. - */ - var merge = false + private val logger = Logger.getLogger(BytecodeContext::class.java.name) /** - * Merge integrations into the [BytecodeContext] and flush all [Integrations]. + * [Opcodes] of the supplied [PatcherOptions.inputFile]. */ - override fun flush() { - if (!merge) return + internal lateinit var opcodes: Opcodes - logger.info("Merging integrations") - - val classMap = classes.associateBy { it.type } - - this@Integrations.forEach { integrations -> + /** + * The list of classes. + */ + val classes by lazy { + ProxyClassList( MultiDexIO.readDexFile( true, - integrations, BasicDexFileNamer(), + options.inputFile, + BasicDexFileNamer(), null, - null - ).classes.forEach classDef@{ classDef -> - val existingClass = classMap[classDef.type] ?: run { - logger.fine("Adding $classDef") - classes.add(classDef) - return@classDef - } + null, + ).also { opcodes = it.opcodes }.classes.toMutableSet(), + ) + } - logger.fine("$classDef exists. Adding missing methods and fields.") + /** + * The [Integrations] of this [PatcherContext]. + */ + internal val integrations = Integrations() - existingClass.merge(classDef, this@BytecodeContext).let { mergedClass -> - // If the class was merged, replace the original class with the merged class. - if (mergedClass === existingClass) return@let - classes.apply { remove(existingClass); add(mergedClass) } + /** + * Find a class by a given class name. + * + * @param className The name of the class. + * @return A proxy for the first class that matches the class name. + */ + fun findClass(className: String) = findClass { it.type.contains(className) } + + /** + * Find a class by a given predicate. + * + * @param predicate A predicate to match the class. + * @return A proxy for the first class that matches the predicate. + */ + fun findClass(predicate: (ClassDef) -> Boolean) = + // if we already proxied the class matching the predicate... + classes.proxies.firstOrNull { predicate(it.immutableClass) } + ?: // else resolve the class to a proxy and return it, if the predicate is matching a class + classes.find(predicate)?.let { proxy(it) } + + /** + * Proxy a class. + * This will allow the class to be modified. + * + * @param classDef The class to proxy. + * @return A proxy for the class. + */ + fun proxy(classDef: ClassDef) = + this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let { + ClassProxy(classDef).also { this.classes.add(it) } + } + + /** + * Create a [MethodWalker] instance for the current [BytecodeContext]. + * + * @param startMethod The method to start at. + * @return A [MethodWalker] instance. + */ + fun toMethodWalker(startMethod: Method) = MethodWalker(this, startMethod) + + /** + * Compile bytecode from the [BytecodeContext]. + * + * @return The compiled bytecode. + */ + override fun get(): List { + logger.info("Compiling patched dex files") + + val patchedDexFileResults = + options.resourceCachePath.resolve("dex").also { + it.deleteRecursively() // Make sure the directory is empty. + it.mkdirs() + }.apply { + MultiDexIO.writeDexFile( + true, + if (options.multithreadingDexFileWriter) -1 else 1, + this, + BasicDexFileNamer(), + object : DexFile { + override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses) + + override fun getOpcodes() = this@BytecodeContext.opcodes + }, + DexIO.DEFAULT_MAX_DEX_POOL_SIZE, + ) { _, entryName, _ -> logger.info("Compiled $entryName") } + }.listFiles(FileFilter { it.isFile })!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) } + + System.gc() + + return patchedDexFileResults + } + + /** + * The integrations of a [PatcherContext]. + */ + internal inner class Integrations : MutableList by mutableListOf(), Flushable { + /** + * Whether to merge integrations. + * Set to true, if the field requiresIntegrations of any supplied [Patch] is true. + */ + var merge = false + + /** + * Merge integrations into the [BytecodeContext] and flush all [Integrations]. + */ + override fun flush() { + if (!merge) return + + logger.info("Merging integrations") + + val classMap = classes.associateBy { it.type } + + this@Integrations.forEach { integrations -> + MultiDexIO.readDexFile( + true, + integrations, + BasicDexFileNamer(), + null, + null, + ).classes.forEach classDef@{ classDef -> + val existingClass = + classMap[classDef.type] ?: run { + logger.fine("Adding $classDef") + classes.add(classDef) + return@classDef + } + + logger.fine("$classDef exists. Adding missing methods and fields.") + + existingClass.merge(classDef, this@BytecodeContext).let { mergedClass -> + // If the class was merged, replace the original class with the merged class. + if (mergedClass === existingClass) return@let + classes.apply { + remove(existingClass) + add(mergedClass) + } + } } } + clear() } - clear() } } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patcher/data/Context.kt b/src/main/kotlin/app/revanced/patcher/data/Context.kt index d56fb07..50c152c 100644 --- a/src/main/kotlin/app/revanced/patcher/data/Context.kt +++ b/src/main/kotlin/app/revanced/patcher/data/Context.kt @@ -6,4 +6,4 @@ import java.util.function.Supplier * A common interface for contexts such as [ResourceContext] and [BytecodeContext]. */ -sealed interface Context : Supplier \ No newline at end of file +sealed interface Context : Supplier diff --git a/src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt b/src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt index f6eb877..d94c108 100644 --- a/src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt +++ b/src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt @@ -26,7 +26,7 @@ import java.util.logging.Logger */ class ResourceContext internal constructor( private val context: PatcherContext, - private val options: PatcherOptions + private val options: PatcherOptions, ) : Context, Iterable { private val logger = Logger.getLogger(ResourceContext::class.java.name) @@ -37,52 +37,54 @@ class ResourceContext internal constructor( * * @param mode The [ResourceDecodingMode] to use when decoding. */ - internal fun decodeResources(mode: ResourceDecodingMode) = with(context.packageMetadata.apkInfo) { - // Needed to decode resources. - val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this) + internal fun decodeResources(mode: ResourceDecodingMode) = + with(context.packageMetadata.apkInfo) { + // Needed to decode resources. + val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this) - when (mode) { - ResourceDecodingMode.FULL -> { - val outDir = options.recreateResourceCacheDirectory() + when (mode) { + ResourceDecodingMode.FULL -> { + val outDir = options.recreateResourceCacheDirectory() - logger.info("Decoding resources") + logger.info("Decoding resources") - resourcesDecoder.decodeResources(outDir) - resourcesDecoder.decodeManifest(outDir) + resourcesDecoder.decodeResources(outDir) + resourcesDecoder.decodeManifest(outDir) - // Needed to record uncompressed files. - val apkDecoder = ApkDecoder(options.resourceConfig, this) - apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping) + // Needed to record uncompressed files. + val apkDecoder = ApkDecoder(options.resourceConfig, this) + apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping) - usesFramework = UsesFramework().apply { - ids = resourcesDecoder.resTable.listFramePackages().map { it.id } - } - } - - ResourceDecodingMode.MANIFEST_ONLY -> { - logger.info("Decoding app manifest") - - // Decode manually instead of using resourceDecoder.decodeManifest - // because it does not support decoding to an OutputStream. - XmlPullStreamDecoder( - AndroidManifestResourceParser(resourcesDecoder.resTable), - resourcesDecoder.resXmlSerializer - ).decodeManifest( - apkFile.directory.getFileInput("AndroidManifest.xml"), - // Older Android versions do not support OutputStream.nullOutputStream() - object : OutputStream() { - override fun write(b: Int) { /* do nothing */ + usesFramework = + UsesFramework().apply { + ids = resourcesDecoder.resTable.listFramePackages().map { it.id } } - } - ) + } - // Get the package name and version from the manifest using the XmlPullStreamDecoder. - // XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo. - context.packageMetadata.let { metadata -> - metadata.packageName = resourcesDecoder.resTable.packageRenamed - versionInfo.let { - metadata.packageVersion = it.versionName ?: it.versionCode - } + ResourceDecodingMode.MANIFEST_ONLY -> { + logger.info("Decoding app manifest") + + // Decode manually instead of using resourceDecoder.decodeManifest + // because it does not support decoding to an OutputStream. + XmlPullStreamDecoder( + AndroidManifestResourceParser(resourcesDecoder.resTable), + resourcesDecoder.resXmlSerializer, + ).decodeManifest( + apkFile.directory.getFileInput("AndroidManifest.xml"), + // Older Android versions do not support OutputStream.nullOutputStream() + object : OutputStream() { + override fun write(b: Int) { // do nothing + } + }, + ) + + // Get the package name and version from the manifest using the XmlPullStreamDecoder. + // XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo. + context.packageMetadata.let { metadata -> + metadata.packageName = resourcesDecoder.resTable.packageRenamed + versionInfo.let { + metadata.packageVersion = it.versionName ?: it.versionCode + } /* The ResTable if flagged as sparse if the main package is not loaded, which is the case here, @@ -92,18 +94,16 @@ class ResourceContext internal constructor( Set this to false again to prevent the ResTable from being flagged as sparse falsely. */ - metadata.apkInfo.sparseResources = false + metadata.apkInfo.sparseResources = false + } } } } - } - operator fun get(path: String) = options.resourceCachePath.resolve(path) override fun iterator() = options.resourceCachePath.walkTopDown().iterator() - /** * Compile resources from the [ResourceContext]. * @@ -116,14 +116,17 @@ class ResourceContext internal constructor( logger.info("Compiling modified resources") val cacheDirectory = ExtFile(options.resourceCachePath) - val aaptFile = cacheDirectory.resolve("aapt_temp_file").also { - Files.deleteIfExists(it.toPath()) - }.also { resourceFile = it } + val aaptFile = + cacheDirectory.resolve("aapt_temp_file").also { + Files.deleteIfExists(it.toPath()) + }.also { resourceFile = it } try { AaptInvoker( - options.resourceConfig, context.packageMetadata.apkInfo - ).invokeAapt(aaptFile, + options.resourceConfig, + context.packageMetadata.apkInfo, + ).invokeAapt( + aaptFile, cacheDirectory.resolve("AndroidManifest.xml").also { ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it) }, @@ -134,7 +137,8 @@ class ResourceContext internal constructor( usesFramework.ids.map { id -> Framework(options.resourceConfig).getFrameworkApk(id, usesFramework.tag) }.toTypedArray() - }) + }, + ) } finally { cacheDirectory.close() } @@ -159,12 +163,10 @@ class ResourceContext internal constructor( } inner class XmlFileHolder { - operator fun get(inputStream: InputStream) = - DomFileEditor(inputStream) + operator fun get(inputStream: InputStream) = DomFileEditor(inputStream) operator fun get(path: String): DomFileEditor { return DomFileEditor(this@ResourceContext[path]) } - } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt index 290174c..3eff3af 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/AnnotationExtensions.kt @@ -11,11 +11,13 @@ internal object AnnotationExtensions { */ fun Class<*>.findAnnotationRecursively(targetAnnotation: KClass): T? { fun Class<*>.findAnnotationRecursively( - targetAnnotation: Class, traversed: MutableSet + targetAnnotation: Class, + traversed: MutableSet, ): T? { val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name } - @Suppress("UNCHECKED_CAST") if (found != null) return found as T + @Suppress("UNCHECKED_CAST") + if (found != null) return found as T for (annotation in this.annotations) { if (traversed.contains(annotation)) continue @@ -30,4 +32,4 @@ internal object AnnotationExtensions { return this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf()) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt index baf7f54..dd3207e 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Extensions.kt @@ -30,4 +30,4 @@ infix fun Int.or(other: AccessFlags) = this or other.value * * @param other The [AccessFlags] to perform the operation with. */ -infix fun AccessFlags.or(other: Int) = value or other \ No newline at end of file +infix fun AccessFlags.or(other: Int) = value or other diff --git a/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt index d915f53..736146b 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/InstructionExtensions.kt @@ -12,7 +12,6 @@ import com.android.tools.smali.dexlib2.builder.instruction.* import com.android.tools.smali.dexlib2.iface.instruction.Instruction object InstructionExtensions { - /** * Add instructions to a method at the given index. * @@ -21,7 +20,7 @@ object InstructionExtensions { */ fun MutableMethodImplementation.addInstructions( index: Int, - instructions: List + instructions: List, ) = instructions.asReversed().forEach { addInstruction(index, it) } /** @@ -39,7 +38,10 @@ object InstructionExtensions { * @param index The index to remove the instructions at. * @param count The amount of instructions to remove. */ - fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) = repeat(count) { + fun MutableMethodImplementation.removeInstructions( + index: Int, + count: Int, + ) = repeat(count) { removeInstruction(index) } @@ -57,7 +59,10 @@ object InstructionExtensions { * @param index The index to replace the instructions at. * @param instructions The instructions to replace the instructions with. */ - fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List) { + fun MutableMethodImplementation.replaceInstructions( + index: Int, + instructions: List, + ) { // Remove the instructions at the given index. removeInstructions(index, instructions.size) @@ -71,16 +76,17 @@ object InstructionExtensions { * @param index The index to add the instruction at. * @param instruction The instruction to add. */ - fun MutableMethod.addInstruction(index: Int, instruction: BuilderInstruction) = - implementation!!.addInstruction(index, instruction) + fun MutableMethod.addInstruction( + index: Int, + instruction: BuilderInstruction, + ) = implementation!!.addInstruction(index, instruction) /** * Add an instruction to a method. * * @param instruction The instructions to add. */ - fun MutableMethod.addInstruction(instruction: BuilderInstruction) = - implementation!!.addInstruction(instruction) + fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction) /** * Add an instruction to a method at the given index. @@ -88,17 +94,17 @@ object InstructionExtensions { * @param index The index to add the instruction at. * @param smaliInstructions The instruction to add. */ - fun MutableMethod.addInstruction(index: Int, smaliInstructions: String) = - implementation!!.addInstruction(index, smaliInstructions.toInstruction(this)) + fun MutableMethod.addInstruction( + index: Int, + smaliInstructions: String, + ) = implementation!!.addInstruction(index, smaliInstructions.toInstruction(this)) /** * Add an instruction to a method. * * @param smaliInstructions The instruction to add. */ - fun MutableMethod.addInstruction(smaliInstructions: String) = - implementation!!.addInstruction(smaliInstructions.toInstruction(this)) - + fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstruction(smaliInstructions.toInstruction(this)) /** * Add instructions to a method at the given index. @@ -106,32 +112,34 @@ object InstructionExtensions { * @param index The index to add the instructions at. * @param instructions The instructions to add. */ - fun MutableMethod.addInstructions(index: Int, instructions: List) = - implementation!!.addInstructions(index, instructions) + fun MutableMethod.addInstructions( + index: Int, + instructions: List, + ) = implementation!!.addInstructions(index, instructions) /** * Add instructions to a method. * * @param instructions The instructions to add. */ - fun MutableMethod.addInstructions(instructions: List) = - implementation!!.addInstructions(instructions) + fun MutableMethod.addInstructions(instructions: List) = implementation!!.addInstructions(instructions) /** * Add instructions to a method. * * @param smaliInstructions The instructions to add. */ - fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) = - implementation!!.addInstructions(index, smaliInstructions.toInstructions(this)) + fun MutableMethod.addInstructions( + index: Int, + smaliInstructions: String, + ) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this)) /** * Add instructions to a method. * * @param smaliInstructions The instructions to add. */ - fun MutableMethod.addInstructions(smaliInstructions: String) = - implementation!!.addInstructions(smaliInstructions.toInstructions(this)) + fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this)) /** * Add instructions to a method at the given index. @@ -144,14 +152,15 @@ object InstructionExtensions { fun MutableMethod.addInstructionsWithLabels( index: Int, smaliInstructions: String, - vararg externalLabels: ExternalLabel + vararg externalLabels: ExternalLabel, ) { // Create reference dummy instructions for the instructions. - val nopSmali = StringBuilder(smaliInstructions).also { builder -> - externalLabels.forEach { (name, _) -> - builder.append("\n:$name\nnop") - } - }.toString() + val nopSmali = + StringBuilder(smaliInstructions).also { builder -> + externalLabels.forEach { (name, _) -> + builder.append("\n:$name\nnop") + } + }.toString() // Compile the instructions with the dummy labels val compiledInstructions = nopSmali.toInstructions(this) @@ -159,7 +168,7 @@ object InstructionExtensions { // Add the compiled list of instructions to the method. addInstructions( index, - compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size) + compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size), ) implementation!!.apply { @@ -174,22 +183,24 @@ object InstructionExtensions { */ fun Instruction.makeNewLabel() { fun replaceOffset( - i: BuilderOffsetInstruction, label: Label + i: BuilderOffsetInstruction, + label: Label, ): BuilderOffsetInstruction { return when (i) { is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label) is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label) is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label) - is BuilderInstruction22t -> BuilderInstruction22t( - i.opcode, - i.registerA, - i.registerB, - label - ) + is BuilderInstruction22t -> + BuilderInstruction22t( + i.opcode, + i.registerA, + i.registerB, + label, + ) is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label) is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label) else -> throw IllegalStateException( - "A non-offset instruction was given, this should never happen!" + "A non-offset instruction was given, this should never happen!", ) } } @@ -198,9 +209,11 @@ object InstructionExtensions { val label = newLabelForIndex(this@apply.instructions.indexOf(this)) // Create the final instruction with the new label. - val newInstruction = replaceOffset( - compiledInstruction, label - ) + val newInstruction = + replaceOffset( + compiledInstruction, + label, + ) // Replace the instruction pointing to the dummy label // with the new instruction pointing to the real instruction. @@ -233,8 +246,7 @@ object InstructionExtensions { * * @param index The index to remove the instruction at. */ - fun MutableMethod.removeInstruction(index: Int) = - implementation!!.removeInstruction(index) + fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index) /** * Remove instructions at the given index. @@ -242,16 +254,17 @@ object InstructionExtensions { * @param index The index to remove the instructions at. * @param count The amount of instructions to remove. */ - fun MutableMethod.removeInstructions(index: Int, count: Int) = - implementation!!.removeInstructions(index, count) + fun MutableMethod.removeInstructions( + index: Int, + count: Int, + ) = implementation!!.removeInstructions(index, count) /** * Remove instructions at the given index. * * @param count The amount of instructions to remove. */ - fun MutableMethod.removeInstructions(count: Int) = - implementation!!.removeInstructions(count) + fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count) /** * Replace an instruction at the given index. @@ -259,8 +272,10 @@ object InstructionExtensions { * @param index The index to replace the instruction at. * @param instruction The instruction to replace the instruction with. */ - fun MutableMethod.replaceInstruction(index: Int, instruction: BuilderInstruction) = - implementation!!.replaceInstruction(index, instruction) + fun MutableMethod.replaceInstruction( + index: Int, + instruction: BuilderInstruction, + ) = implementation!!.replaceInstruction(index, instruction) /** * Replace an instruction at the given index. @@ -268,8 +283,10 @@ object InstructionExtensions { * @param index The index to replace the instruction at. * @param smaliInstruction The smali instruction to replace the instruction with. */ - fun MutableMethod.replaceInstruction(index: Int, smaliInstruction: String) = - implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this)) + fun MutableMethod.replaceInstruction( + index: Int, + smaliInstruction: String, + ) = implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this)) /** * Replace instructions at the given index. @@ -277,8 +294,10 @@ object InstructionExtensions { * @param index The index to replace the instructions at. * @param instructions The instructions to replace the instructions with. */ - fun MutableMethod.replaceInstructions(index: Int, instructions: List) = - implementation!!.replaceInstructions(index, instructions) + fun MutableMethod.replaceInstructions( + index: Int, + instructions: List, + ) = implementation!!.replaceInstructions(index, instructions) /** * Replace instructions at the given index. @@ -286,8 +305,10 @@ object InstructionExtensions { * @param index The index to replace the instructions at. * @param smaliInstructions The smali instructions to replace the instructions with. */ - fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) = - implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this)) + fun MutableMethod.replaceInstructions( + index: Int, + smaliInstructions: String, + ) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this)) /** * Get an instruction at the given index. @@ -327,4 +348,4 @@ object InstructionExtensions { * @return The instructions. */ fun MutableMethod.getInstructions(): MutableList = implementation!!.instructions -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt b/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt index f0906f8..25dc694 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/MethodFingerprintExtensions.kt @@ -1,8 +1,8 @@ package app.revanced.patcher.extensions import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively -import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod object MethodFingerprintExtensions { // TODO: Make this a property. @@ -11,4 +11,4 @@ object MethodFingerprintExtensions { */ val MethodFingerprint.fuzzyPatternScanMethod get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/LookupMap.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/LookupMap.kt index 80847e7..6497cd5 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/LookupMap.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/LookupMap.kt @@ -20,7 +20,7 @@ internal class LookupMap : MutableMap by muta */ fun add( key: String, - methodClassPair: MethodClassPair + methodClassPair: MethodClassPair, ) { getOrPut(key) { MethodClassList() }.add(methodClassPair) } @@ -73,13 +73,14 @@ internal class LookupMap : MutableMap by muta append(accessFlagsReturnKey) appendParameters(method.parameterTypes) }, - methodClassPair + methodClassPair, ) // Add strings contained in the method as the key. method.implementation?.instructions?.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) { return@instructions + } val string = ((instruction as ReferenceInstruction).reference as StringReference).string @@ -120,6 +121,5 @@ internal class LookupMap : MutableMap by muta append(parameter.first()) } } - } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt index a2e0e3f..40973d9 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprint.kt @@ -34,7 +34,7 @@ abstract class MethodFingerprint( internal val parameters: Iterable? = null, internal val opcodes: Iterable? = null, internal val strings: Iterable? = null, - internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null + internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null, ) { /** * The result of the [MethodFingerprint]. @@ -86,11 +86,12 @@ abstract class MethodFingerprint( } } - val key = buildString { - append(accessFlags) - append(returnTypeValue.first()) - if (parameters != null) appendParameters(parameters) - } + val key = + buildString { + append(accessFlags) + append(returnTypeValue.first()) + if (parameters != null) appendParameters(parameters) + } return methodSignatureLookupMap[key] ?: return LookupMap.MethodClassList() } @@ -107,7 +108,11 @@ abstract class MethodFingerprint( } val methodsWithSameStrings = methodStringsLookup() - if (methodsWithSameStrings != null) if (resolveUsingMethodClassPair(methodsWithSameStrings)) return true + if (methodsWithSameStrings != null) { + if (resolveUsingMethodClassPair(methodsWithSameStrings)) { + return true + } + } // No strings declared or none matched (partial matches are allowed). // Use signature matching. @@ -121,10 +126,14 @@ abstract class MethodFingerprint( * @param context The [BytecodeContext] to host proxies. * @return True if the resolution was successful, false otherwise. */ - fun resolve(context: BytecodeContext, forClass: ClassDef): Boolean { + fun resolve( + context: BytecodeContext, + forClass: ClassDef, + ): Boolean { for (method in forClass.methods) - if (resolve(context, method, forClass)) + if (resolve(context, method, forClass)) { return true + } return false } @@ -136,20 +145,26 @@ abstract class MethodFingerprint( * @param context The [BytecodeContext] to host proxies. * @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise. */ - fun resolve(context: BytecodeContext, method: Method, forClass: ClassDef): Boolean { + fun resolve( + context: BytecodeContext, + method: Method, + forClass: ClassDef, + ): Boolean { val methodFingerprint = this if (methodFingerprint.result != null) return true - if (methodFingerprint.returnType != null && !method.returnType.startsWith(methodFingerprint.returnType)) + if (methodFingerprint.returnType != null && !method.returnType.startsWith(methodFingerprint.returnType)) { return false + } - if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags) + if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags) { return false - + } fun parametersEqual( - parameters1: Iterable, parameters2: Iterable + parameters1: Iterable, + parameters2: Iterable, ): Boolean { if (parameters1.count() != parameters2.count()) return false val iterator1 = parameters1.iterator() @@ -159,15 +174,19 @@ abstract class MethodFingerprint( return true } - if (methodFingerprint.parameters != null && !parametersEqual( + if (methodFingerprint.parameters != null && + !parametersEqual( methodFingerprint.parameters, // TODO: parseParameters() - method.parameterTypes + method.parameterTypes, ) - ) return false + ) { + return false + } @Suppress("UNNECESSARY_NOT_NULL_ASSERTION") - if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass)) + if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass)) { return false + } val stringsScanResult: StringsScanResult? = if (methodFingerprint.strings != null) { @@ -181,7 +200,9 @@ abstract class MethodFingerprint( if ( instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO - ) return@forEachIndexed + ) { + return@forEachIndexed + } val string = ((instruction as ReferenceInstruction).reference as StringReference).string val index = stringsList.indexOfFirst(string::contains) @@ -192,91 +213,98 @@ abstract class MethodFingerprint( } if (stringsList.isNotEmpty()) return false - } + }, ) - } else null - - val patternScanResult = if (methodFingerprint.opcodes != null) { - method.implementation?.instructions ?: return false - - fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.newWarnings( - pattern: Iterable, instructions: Iterable - ) = buildList { - for ((patternIndex, instructionIndex) in (this@newWarnings.startIndex until this@newWarnings.endIndex).withIndex()) { - val originalOpcode = instructions.elementAt(instructionIndex).opcode - val patternOpcode = pattern.elementAt(patternIndex) - - if (patternOpcode == null || patternOpcode.ordinal == originalOpcode.ordinal) continue - - this.add( - MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.Warning( - originalOpcode, - patternOpcode, - instructionIndex, - patternIndex - ) - ) - } + } else { + null } - fun Method.patternScan( - fingerprint: MethodFingerprint - ): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? { - val instructions = this.implementation!!.instructions - val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0 + val patternScanResult = + if (methodFingerprint.opcodes != null) { + method.implementation?.instructions ?: return false - val pattern = fingerprint.opcodes!! - val instructionLength = instructions.count() - val patternLength = pattern.count() - - for (index in 0 until instructionLength) { - var patternIndex = 0 - var threshold = fingerprintFuzzyPatternScanThreshold - - while (index + patternIndex < instructionLength) { - val originalOpcode = instructions.elementAt(index + patternIndex).opcode + fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.newWarnings( + pattern: Iterable, + instructions: Iterable, + ) = buildList { + for ((patternIndex, instructionIndex) in (this@newWarnings.startIndex until this@newWarnings.endIndex).withIndex()) { + val originalOpcode = instructions.elementAt(instructionIndex).opcode val patternOpcode = pattern.elementAt(patternIndex) - if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) { - // reaching maximum threshold (0) means, - // the pattern does not match to the current instructions - if (threshold-- == 0) break - } + if (patternOpcode == null || patternOpcode.ordinal == originalOpcode.ordinal) continue - if (patternIndex < patternLength - 1) { - // if the entire pattern has not been scanned yet - // continue the scan - patternIndex++ - continue - } - // the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod - val result = - MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult( - index, - index + patternIndex - ) - if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result - result.warnings = result.newWarnings(pattern, instructions) - - return result + this.add( + MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.Warning( + originalOpcode, + patternOpcode, + instructionIndex, + patternIndex, + ), + ) } } - return null + fun Method.patternScan( + fingerprint: MethodFingerprint, + ): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? { + val instructions = this.implementation!!.instructions + val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0 + + val pattern = fingerprint.opcodes!! + val instructionLength = instructions.count() + val patternLength = pattern.count() + + for (index in 0 until instructionLength) { + var patternIndex = 0 + var threshold = fingerprintFuzzyPatternScanThreshold + + while (index + patternIndex < instructionLength) { + val originalOpcode = instructions.elementAt(index + patternIndex).opcode + val patternOpcode = pattern.elementAt(patternIndex) + + if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) { + // reaching maximum threshold (0) means, + // the pattern does not match to the current instructions + if (threshold-- == 0) break + } + + if (patternIndex < patternLength - 1) { + // if the entire pattern has not been scanned yet + // continue the scan + patternIndex++ + continue + } + // the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod + val result = + MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult( + index, + index + patternIndex, + ) + if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result + result.warnings = result.newWarnings(pattern, instructions) + + return result + } + } + + return null + } + + method.patternScan(methodFingerprint) ?: return false + } else { + null } - method.patternScan(methodFingerprint) ?: return false - } else null - - methodFingerprint.result = MethodFingerprintResult( - method, - forClass, - MethodFingerprintResult.MethodFingerprintScanResult( - patternScanResult, - stringsScanResult - ), - context - ) + methodFingerprint.result = + MethodFingerprintResult( + method, + forClass, + MethodFingerprintResult.MethodFingerprintScanResult( + patternScanResult, + stringsScanResult, + ), + context, + ) return true } @@ -309,12 +337,13 @@ abstract class MethodFingerprint( * @param context The [BytecodeContext] to host proxies. * @return True if the resolution was successful, false otherwise. */ - fun Iterable.resolve(context: BytecodeContext, classes: Iterable) = - forEach { fingerprint -> - for (classDef in classes) { - if (fingerprint.resolve(context, classDef)) break - } + fun Iterable.resolve( + context: BytecodeContext, + classes: Iterable, + ) = forEach { fingerprint -> + for (classDef in classes) { + if (fingerprint.resolve(context, classDef)) break } + } } } - diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprintResult.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprintResult.kt index 318e1a0..2505003 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprintResult.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/MethodFingerprintResult.kt @@ -20,7 +20,7 @@ class MethodFingerprintResult( val method: Method, val classDef: ClassDef, val scanResult: MethodFingerprintScanResult, - internal val context: BytecodeContext + internal val context: BytecodeContext, ) { /** * Returns a mutable clone of [classDef] @@ -50,7 +50,7 @@ class MethodFingerprintResult( */ class MethodFingerprintScanResult( val patternScanResult: PatternScanResult?, - val stringsScanResult: StringsScanResult? + val stringsScanResult: StringsScanResult?, ) { /** * The result of scanning strings on the [MethodFingerprint]. @@ -74,7 +74,7 @@ class MethodFingerprintResult( class PatternScanResult( val startIndex: Int, val endIndex: Int, - var warnings: List? = null + var warnings: List? = null, ) { /** * Represents warnings of the pattern scan. @@ -91,4 +91,4 @@ class MethodFingerprintResult( ) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/annotation/MethodFingerprintAnnotations.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/annotation/MethodFingerprintAnnotations.kt index e0d5121..dbaebac 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/annotation/MethodFingerprintAnnotations.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/annotation/MethodFingerprintAnnotations.kt @@ -8,5 +8,5 @@ import app.revanced.patcher.fingerprint.MethodFingerprint */ @Target(AnnotationTarget.CLASS) annotation class FuzzyPatternScanMethod( - val threshold: Int = 1 -) \ No newline at end of file + val threshold: Int = 1, +) diff --git a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt index c2d1930..d13c8a5 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt @@ -9,5 +9,5 @@ import app.revanced.patcher.fingerprint.MethodFingerprint * @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed. */ abstract class BytecodePatch( - internal val fingerprints : Set = emptySet(), -) : Patch() \ No newline at end of file + internal val fingerprints: Set = emptySet(), +) : Patch() diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index f962505..28e9b17 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -47,7 +47,6 @@ sealed class Patch> { var use = true private set - // TODO: Remove this property, once integrations are coupled with patches. /** * Weather or not the patch requires integrations. @@ -64,9 +63,10 @@ sealed class Patch> { this::class.findAnnotation()?.let { annotation -> name = annotation.name.ifEmpty { null } description = annotation.description.ifEmpty { null } - compatiblePackages = annotation.compatiblePackages - .map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) } - .toSet().ifEmpty { null } + compatiblePackages = + annotation.compatiblePackages + .map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) } + .toSet().ifEmpty { null } dependencies = annotation.dependencies.toSet().ifEmpty { null } use = annotation.use requiresIntegrations = annotation.requiresIntegrations @@ -104,4 +104,4 @@ sealed class Patch> { val name: String, val versions: Set? = null, ) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/patch/PatchException.kt b/src/main/kotlin/app/revanced/patcher/patch/PatchException.kt index 962e276..544859f 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/PatchException.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/PatchException.kt @@ -9,4 +9,4 @@ package app.revanced.patcher.patch class PatchException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) { constructor(errorMessage: String) : this(errorMessage, null) constructor(cause: Throwable) : this(cause.message, cause) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/patch/PatchResult.kt b/src/main/kotlin/app/revanced/patcher/patch/PatchResult.kt index cf25fbe..790f419 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/PatchResult.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/PatchResult.kt @@ -6,4 +6,4 @@ package app.revanced.patcher.patch * @param patch The [Patch] that was executed. * @param exception The [PatchException] thrown, if any. */ -class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) \ No newline at end of file +class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) diff --git a/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt b/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt index 368bbb8..e86ebfd 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/ResourcePatch.kt @@ -5,4 +5,4 @@ import app.revanced.patcher.data.ResourceContext /** * A ReVanced [Patch] that works on [ResourceContext]. */ -abstract class ResourcePatch : Patch() \ No newline at end of file +abstract class ResourcePatch : Patch() diff --git a/src/main/kotlin/app/revanced/patcher/patch/annotation/PatchAnnotations.kt b/src/main/kotlin/app/revanced/patcher/patch/annotation/PatchAnnotations.kt index f003e45..2e81f5a 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/annotation/PatchAnnotations.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/annotation/PatchAnnotations.kt @@ -34,4 +34,4 @@ annotation class Patch( annotation class CompatiblePackage( val name: String, val versions: Array = [], -) \ No newline at end of file +) diff --git a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOption.kt b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOption.kt index d9522ec..ad0f6fe 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOption.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOption.kt @@ -25,7 +25,7 @@ open class PatchOption( val description: String?, val required: Boolean, val valueType: String, - val validator: PatchOption.(T?) -> Boolean + val validator: PatchOption.(T?) -> Boolean, ) { /** * The value of the [PatchOption]. @@ -45,6 +45,7 @@ open class PatchOption( uncheckedValue = value } + /** * Get the value of the [PatchOption]. * @@ -81,9 +82,16 @@ open class PatchOption( override fun toString() = value.toString() - operator fun getValue(thisRef: Any?, property: KProperty<*>) = value + operator fun getValue( + thisRef: Any?, + property: KProperty<*>, + ) = value - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + operator fun setValue( + thisRef: Any?, + property: KProperty<*>, + value: T?, + ) { this.value = value } @@ -111,9 +119,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption.(String?) -> Boolean = { true } + validator: PatchOption.(String?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "String", validator + key, + default, + values, + title, + description, + required, + "String", + validator, ).also { registerOption(it) } /** @@ -138,9 +153,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption.(Int?) -> Boolean = { true } + validator: PatchOption.(Int?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "Int", validator + key, + default, + values, + title, + description, + required, + "Int", + validator, ).also { registerOption(it) } /** @@ -165,7 +187,7 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption.(Boolean?) -> Boolean = { true } + validator: PatchOption.(Boolean?) -> Boolean = { true }, ) = PatchOption(key, default, values, title, description, required, "Boolean", validator).also { registerOption(it) } @@ -192,9 +214,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption.(Float?) -> Boolean = { true } + validator: PatchOption.(Float?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "Float", validator + key, + default, + values, + title, + description, + required, + "Float", + validator, ).also { registerOption(it) } /** @@ -219,9 +248,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption.(Long?) -> Boolean = { true } + validator: PatchOption.(Long?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "Long", validator + key, + default, + values, + title, + description, + required, + "Long", + validator, ).also { registerOption(it) } /** @@ -246,9 +282,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption?>.(Array?) -> Boolean = { true } + validator: PatchOption?>.(Array?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "StringArray", validator + key, + default, + values, + title, + description, + required, + "StringArray", + validator, ).also { registerOption(it) } /** @@ -273,9 +316,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption?>.(Array?) -> Boolean = { true } + validator: PatchOption?>.(Array?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "IntArray", validator + key, + default, + values, + title, + description, + required, + "IntArray", + validator, ).also { registerOption(it) } /** @@ -300,9 +350,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption?>.(Array?) -> Boolean = { true } + validator: PatchOption?>.(Array?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "BooleanArray", validator + key, + default, + values, + title, + description, + required, + "BooleanArray", + validator, ).also { registerOption(it) } /** @@ -327,9 +384,16 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption?>.(Array?) -> Boolean = { true } + validator: PatchOption?>.(Array?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "FloatArray", validator + key, + default, + values, + title, + description, + required, + "FloatArray", + validator, ).also { registerOption(it) } /** @@ -354,11 +418,18 @@ open class PatchOption( title: String? = null, description: String? = null, required: Boolean = false, - validator: PatchOption?>.(Array?) -> Boolean = { true } + validator: PatchOption?>.(Array?) -> Boolean = { true }, ) = PatchOption( - key, default, values, title, description, required, "LongArray", validator + key, + default, + values, + title, + description, + required, + "LongArray", + validator, ).also { registerOption(it) } private fun

> P.registerOption(option: PatchOption<*>) = option.also { options.register(it) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptionException.kt b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptionException.kt index 676f6e1..4b5dd99 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptionException.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptionException.kt @@ -36,6 +36,6 @@ sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage * * @param key The key of the [PatchOption]. */ - class PatchOptionNotFoundException(key: String) - : PatchOptionException("No option with key $key") -} \ No newline at end of file + class PatchOptionNotFoundException(key: String) : + PatchOptionException("No option with key $key") +} diff --git a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptions.kt b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptions.kt index 481da72..69f30ea 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptions.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/options/PatchOptions.kt @@ -1,13 +1,12 @@ package app.revanced.patcher.patch.options - /** * A map of [PatchOption]s associated by their keys. * * @param options The [PatchOption]s to initialize with. */ class PatchOptions internal constructor( - private val options: MutableMap> = mutableMapOf() + private val options: MutableMap> = mutableMapOf(), ) : MutableMap> by options { /** * Register a [PatchOption]. Acts like [MutableMap.put]. @@ -23,7 +22,10 @@ class PatchOptions internal constructor( * @param value The value. * @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist. */ - operator fun set(key: String, value: T?) { + operator fun set( + key: String, + value: T?, + ) { val option = this[key] try { @@ -40,6 +42,5 @@ class PatchOptions internal constructor( /** * Get an option. */ - override operator fun get(key: String) = - options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key) -} \ No newline at end of file + override operator fun get(key: String) = options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key) +} diff --git a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt index 37fb9df..87789b1 100644 --- a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt +++ b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt @@ -34,9 +34,12 @@ internal object ClassMerger { * @param context The context to traverse the class hierarchy in. * @return The merged class or the original class if no merge was needed. */ - fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext) = this - //.fixFieldAccess(otherClass) - //.fixMethodAccess(otherClass) + fun ClassDef.merge( + otherClass: ClassDef, + context: BytecodeContext, + ) = this + // .fixFieldAccess(otherClass) + // .fixMethodAccess(otherClass) .addMissingFields(otherClass) .addMissingMethods(otherClass) .publicize(otherClass, context) @@ -47,13 +50,14 @@ internal object ClassMerger { * @param fromClass The class to add missing methods from. */ private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef { - val missingMethods = fromClass.methods.let { fromMethods -> - methods.filterNot { method -> - fromMethods.any { fromMethod -> - MethodUtil.methodSignaturesMatch(fromMethod, method) + val missingMethods = + fromClass.methods.let { fromMethods -> + methods.filterNot { method -> + fromMethods.any { fromMethod -> + MethodUtil.methodSignaturesMatch(fromMethod, method) + } } } - } if (missingMethods.isEmpty()) return this @@ -70,9 +74,10 @@ internal object ClassMerger { * @param fromClass The class to add missing fields from. */ private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef { - val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField -> - fromField.name == field.name - } + val missingFields = + fields.filterNotAny(fromClass.fields) { field, fromField -> + fromField.name == field.name + } if (missingFields.isEmpty()) return this @@ -88,18 +93,22 @@ internal object ClassMerger { * @param reference The class to check the [AccessFlags] of. * @param context The context to traverse the class hierarchy in. */ - private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext) = - if (reference.accessFlags.isPublic() && !accessFlags.isPublic()) - this.asMutableClass().apply { - context.traverseClassHierarchy(this) { - if (accessFlags.isPublic()) return@traverseClassHierarchy + private fun ClassDef.publicize( + reference: ClassDef, + context: BytecodeContext, + ) = if (reference.accessFlags.isPublic() && !accessFlags.isPublic()) { + this.asMutableClass().apply { + context.traverseClassHierarchy(this) { + if (accessFlags.isPublic()) return@traverseClassHierarchy - logger.fine("Publicizing ${this.type}") + logger.fine("Publicizing ${this.type}") - accessFlags = accessFlags.toPublic() - } + accessFlags = accessFlags.toPublic() } - else this + } + } else { + this + } /** * Publicize fields if they are public in [reference]. @@ -107,11 +116,12 @@ internal object ClassMerger { * @param reference The class to check the [AccessFlags] of the fields in. */ private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef { - val brokenFields = fields.filterAny(reference.fields) { field, referenceField -> - if (field.name != referenceField.name) return@filterAny false + val brokenFields = + fields.filterAny(reference.fields) { field, referenceField -> + if (field.name != referenceField.name) return@filterAny false - referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic() - } + referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic() + } if (brokenFields.isEmpty()) return this @@ -135,11 +145,12 @@ internal object ClassMerger { * @param reference The class to check the [AccessFlags] of the methods in. */ private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef { - val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod -> - if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false + val brokenMethods = + methods.filterAny(reference.methods) { method, referenceMethod -> + if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false - referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic() - } + referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic() + } if (brokenMethods.isEmpty()) return this @@ -164,7 +175,10 @@ internal object ClassMerger { * @param targetClass the class to start traversing the class hierarchy from * @param callback function that is called for every class in the hierarchy */ - fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { + fun BytecodeContext.traverseClassHierarchy( + targetClass: MutableClass, + callback: MutableClass.() -> Unit, + ) { callback(targetClass) this.findClass(targetClass.superclass ?: return)?.mutableClass?.let { traverseClassHierarchy(it, callback) @@ -195,7 +209,8 @@ internal object ClassMerger { * @return The [this] filtered on [needles] matching the given [predicate]. */ fun Iterable.filterAny( - needles: Iterable, predicate: (HayType, NeedleType) -> Boolean + needles: Iterable, + predicate: (HayType, NeedleType) -> Boolean, ) = Iterable::filter.any(this, needles, predicate) /** @@ -206,17 +221,18 @@ internal object ClassMerger { * @return The [this] filtered on [needles] not matching the given [predicate]. */ fun Iterable.filterNotAny( - needles: Iterable, predicate: (HayType, NeedleType) -> Boolean + needles: Iterable, + predicate: (HayType, NeedleType) -> Boolean, ) = Iterable::filterNot.any(this, needles, predicate) fun KFunction2, (HayType) -> Boolean, List>.any( haystack: Iterable, needles: Iterable, - predicate: (HayType, NeedleType) -> Boolean + predicate: (HayType, NeedleType) -> Boolean, ) = this(haystack) { hay -> needles.any { needle -> predicate(hay, needle) } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt b/src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt index b679dd9..4521afc 100644 --- a/src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt +++ b/src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt @@ -31,9 +31,9 @@ class DomFileEditor internal constructor( /** * The document of the xml file */ - val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream) - .also(Document::normalize) - + val file: Document = + DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream) + .also(Document::normalize) // lazily open an output stream // this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed @@ -58,12 +58,13 @@ class DomFileEditor internal constructor( outputStream?.let { // prevent writing to same file, if it is being locked // isLocked will be false if the editor was created through a stream - val isLocked = filePath?.let { path -> - val isLocked = locks[path]!! > 1 - // decrease the lock count if the editor was opened for a file - locks.merge(path, -1, Integer::sum) - isLocked - } ?: false + val isLocked = + filePath?.let { path -> + val isLocked = locks[path]!! > 1 + // decrease the lock count if the editor was opened for a file + locks.merge(path, -1, Integer::sum) + isLocked + } ?: false // if unlocked, write back to the file if (!isLocked) { @@ -84,4 +85,4 @@ class DomFileEditor internal constructor( // map of concurrent open files val locks = mutableMapOf() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt b/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt index 681bc8f..a05b7b8 100644 --- a/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt +++ b/src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt @@ -19,15 +19,16 @@ class ProxyClassList internal constructor(classes: MutableSet) : Mutab /** * Replace all classes with their mutated versions. */ - internal fun replaceClasses() = proxies.removeIf { proxy -> - // If the proxy is unused, return false to keep it in the proxies list. + internal fun replaceClasses() = + proxies.removeIf { proxy -> + // If the proxy is unused, return false to keep it in the proxies list. if (!proxy.resolved) return@removeIf false - // If it has been used, replace the original class with the mutable class. - remove(proxy.immutableClass) - add(proxy.mutableClass) + // If it has been used, replace the original class with the mutable class. + remove(proxy.immutableClass) + add(proxy.mutableClass) - // Return true to remove the proxy from the proxies list. + // Return true to remove the proxy from the proxies list. return@removeIf true } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt b/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt index a0d2ef0..0f27511 100644 --- a/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt +++ b/src/main/kotlin/app/revanced/patcher/util/method/MethodWalker.kt @@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil */ class MethodWalker internal constructor( private val bytecodeContext: BytecodeContext, - private var currentMethod: Method + private var currentMethod: Method, ) { /** * Get the method which was walked last. @@ -36,7 +36,10 @@ class MethodWalker internal constructor( * @param walkMutable If this is true, the class of the method will be resolved mutably. * @return The same [MethodWalker] instance with the method at [offset]. */ - fun nextMethod(offset: Int, walkMutable: Boolean = false): MethodWalker { + fun nextMethod( + offset: Int, + walkMutable: Boolean = false, + ): MethodWalker { currentMethod.implementation?.instructions?.let { instructions -> val instruction = instructions.elementAt(offset) @@ -44,13 +47,14 @@ class MethodWalker internal constructor( val proxy = bytecodeContext.findClass(newMethod.definingClass)!! val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods - currentMethod = methods.first { - return@first MethodUtil.methodSignaturesMatch(it, newMethod) - } + currentMethod = + methods.first { + return@first MethodUtil.methodSignaturesMatch(it, newMethod) + } return this } throw MethodNotFoundException("This method can not be walked at offset $offset inside the method ${currentMethod.name}") } internal class MethodNotFoundException(exception: String) : Exception(exception) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt index 1c60185..b20c0b7 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/ClassProxy.kt @@ -27,7 +27,8 @@ class ClassProxy internal constructor( resolved = true if (immutableClass is MutableClass) { immutableClass - } else + } else { MutableClass(immutableClass) + } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt index f9e59dc..5bb8bfc 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement.kt @@ -31,4 +31,4 @@ class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnot return MutableAnnotationElement(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt index 5c0c022..19f5f1f 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableClass.kt @@ -3,11 +3,11 @@ package app.revanced.patcher.util.proxy.mutableTypes import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import com.google.common.collect.Iterables import com.android.tools.smali.dexlib2.base.reference.BaseTypeReference import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.util.FieldUtil import com.android.tools.smali.dexlib2.util.MethodUtil +import com.google.common.collect.Iterables class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() { // Class @@ -100,4 +100,4 @@ class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() { return MutableClass(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt index c026956..5d29be7 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/MutableMethod.kt @@ -77,4 +77,4 @@ class MutableMethod(method: Method) : Method, BaseMethodReference() { return MutableMethod(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt index 1609385..1993115 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue.kt @@ -5,7 +5,8 @@ import com.android.tools.smali.dexlib2.base.value.BaseAnnotationEncodedValue import com.android.tools.smali.dexlib2.iface.AnnotationElement import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue -class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : BaseAnnotationEncodedValue(), +class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : + BaseAnnotationEncodedValue(), MutableEncodedValue { private var type = annotationEncodedValue.type diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt index 174ddcc..282b24c 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue.kt @@ -3,7 +3,8 @@ package app.revanced.patcher.util.proxy.mutableTypes.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseBooleanEncodedValue import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue -class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : BaseBooleanEncodedValue(), +class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : + BaseBooleanEncodedValue(), MutableEncodedValue { private var value = booleanEncodedValue.value @@ -20,4 +21,4 @@ class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : Bas return MutableBooleanEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt index 83ec51f..cbb076a 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue.kt @@ -19,4 +19,4 @@ class MutableByteEncodedValue(byteEncodedValue: ByteEncodedValue) : BaseByteEnco return MutableByteEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt index 0683d79..7258c40 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue.kt @@ -3,7 +3,8 @@ package app.revanced.patcher.util.proxy.mutableTypes.encodedValue import com.android.tools.smali.dexlib2.base.value.BaseDoubleEncodedValue import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue -class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDoubleEncodedValue(), +class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : + BaseDoubleEncodedValue(), MutableEncodedValue { private var value = doubleEncodedValue.value @@ -20,4 +21,4 @@ class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDo return MutableDoubleEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt index c4f5c04..aa4cefa 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue.kt @@ -29,4 +29,4 @@ interface MutableEncodedValue : EncodedValue { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt index e66a130..f3a914a 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue.kt @@ -19,4 +19,4 @@ class MutableFloatEncodedValue(floatEncodedValue: FloatEncodedValue) : BaseFloat return MutableFloatEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt index ae81472..db06751 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue.kt @@ -19,4 +19,4 @@ class MutableIntEncodedValue(intEncodedValue: IntEncodedValue) : BaseIntEncodedV return MutableIntEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt index d66ccee..49f5ccc 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue.kt @@ -4,7 +4,8 @@ import com.android.tools.smali.dexlib2.base.value.BaseMethodEncodedValue import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.value.MethodEncodedValue -class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) : BaseMethodEncodedValue(), +class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) : + BaseMethodEncodedValue(), MutableEncodedValue { private var value = methodEncodedValue.value diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt index cbcfac5..aa93bda 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue.kt @@ -22,6 +22,4 @@ class MutableMethodHandleEncodedValue(methodHandleEncodedValue: MethodHandleEnco return MutableMethodHandleEncodedValue(this) } } - - -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt index d11e517..2d67f5f 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue.kt @@ -4,7 +4,8 @@ import com.android.tools.smali.dexlib2.base.value.BaseMethodTypeEncodedValue import com.android.tools.smali.dexlib2.iface.reference.MethodProtoReference import com.android.tools.smali.dexlib2.iface.value.MethodTypeEncodedValue -class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedValue) : BaseMethodTypeEncodedValue(), +class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedValue) : + BaseMethodTypeEncodedValue(), MutableEncodedValue { private var value = methodTypeEncodedValue.value @@ -21,6 +22,4 @@ class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedVal return MutableMethodTypeEncodedValue(this) } } - - -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt index b4305fa..4513ab6 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue.kt @@ -9,4 +9,4 @@ class MutableNullEncodedValue : BaseNullEncodedValue(), MutableEncodedValue { return MutableByteEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt index 351924a..290276d 100644 --- a/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt +++ b/src/main/kotlin/app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue.kt @@ -4,7 +4,8 @@ import com.android.tools.smali.dexlib2.base.value.BaseStringEncodedValue import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue -class MutableStringEncodedValue(stringEncodedValue: StringEncodedValue) : BaseStringEncodedValue(), +class MutableStringEncodedValue(stringEncodedValue: StringEncodedValue) : + BaseStringEncodedValue(), MutableEncodedValue { private var value = stringEncodedValue.value @@ -21,4 +22,4 @@ class MutableStringEncodedValue(stringEncodedValue: StringEncodedValue) : BaseSt return MutableByteEncodedValue(this) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt index 23cd1ec..f2e1503 100644 --- a/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt +++ b/src/main/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompiler.kt @@ -1,9 +1,6 @@ package app.revanced.patcher.util.smali import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import org.antlr.runtime.CommonTokenStream -import org.antlr.runtime.TokenSource -import org.antlr.runtime.tree.CommonTreeNodeStream import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcodes import com.android.tools.smali.dexlib2.builder.BuilderInstruction @@ -12,6 +9,9 @@ import com.android.tools.smali.smali.LexerErrorInterface import com.android.tools.smali.smali.smaliFlexLexer import com.android.tools.smali.smali.smaliParser import com.android.tools.smali.smali.smaliTreeWalker +import org.antlr.runtime.CommonTokenStream +import org.antlr.runtime.TokenSource +import org.antlr.runtime.tree.CommonTreeNodeStream import java.io.InputStreamReader import java.util.Locale @@ -32,15 +32,23 @@ class InlineSmaliCompiler { * if the parameters and registers of the method are passed. */ fun compile( - instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean + instructions: String, + parameters: String, + registers: Int, + forStaticMethod: Boolean, ): List { - val input = METHOD_TEMPLATE.format(Locale.ENGLISH, - if (forStaticMethod) { - "static" - } else { - "" - }, parameters, registers, instructions - ) + val input = + METHOD_TEMPLATE.format( + Locale.ENGLISH, + if (forStaticMethod) { + "static" + } else { + "" + }, + parameters, + registers, + instructions, + ) val reader = InputStreamReader(input.byteInputStream()) val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15) val tokens = CommonTokenStream(lexer as TokenSource) @@ -48,7 +56,7 @@ class InlineSmaliCompiler { val result = parser.smali_file() if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) { throw IllegalStateException( - "Encountered ${parser.numberOfSyntaxErrors} parser syntax errors and ${lexer.numberOfSyntaxErrors} lexer syntax errors!" + "Encountered ${parser.numberOfSyntaxErrors} parser syntax errors and ${lexer.numberOfSyntaxErrors} lexer syntax errors!", ) } val treeStream = CommonTreeNodeStream(result.tree) @@ -70,10 +78,11 @@ class InlineSmaliCompiler { * @returns A list of instructions. */ fun String.toInstructions(method: MutableMethod? = null): List { - return InlineSmaliCompiler.compile(this, + return InlineSmaliCompiler.compile( + this, method?.parameters?.joinToString("") { it } ?: "", method?.implementation?.registerCount ?: 1, - method?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true + method?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true, ) } @@ -82,4 +91,4 @@ fun String.toInstructions(method: MutableMethod? = null): List this.addInstruction(TestInstruction(i)) } - }, - ).let { testMethod = it.toMutable() } + fun createTestMethod() = + ImmutableMethod( + "TestClass;", + "testMethod", + null, + "V", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(16).also { testMethodImplementation = it }.apply { + repeat(10) { i -> this.addInstruction(TestInstruction(i)) } + }, + ).let { testMethod = it.toMutable() } @Test - fun addInstructionsToImplementationIndexed() = applyToImplementation { - addInstructions(5, getTestInstructions(5..6)).also { - assertRegisterIs(5, 5) - assertRegisterIs(6, 6) + fun addInstructionsToImplementationIndexed() = + applyToImplementation { + addInstructions(5, getTestInstructions(5..6)).also { + assertRegisterIs(5, 5) + assertRegisterIs(6, 6) - assertRegisterIs(5, 7) + assertRegisterIs(5, 7) + } } - } @Test - fun addInstructionsToImplementation() = applyToImplementation { - addInstructions(getTestInstructions(10..11)).also { - assertRegisterIs(10, 10) - assertRegisterIs(11, 11) + fun addInstructionsToImplementation() = + applyToImplementation { + addInstructions(getTestInstructions(10..11)).also { + assertRegisterIs(10, 10) + assertRegisterIs(11, 11) + } } - } @Test - fun removeInstructionsFromImplementationIndexed() = applyToImplementation { - removeInstructions(5, 5).also { assertRegisterIs(4, 4) } - } - - @Test - fun removeInstructionsFromImplementation() = applyToImplementation { - removeInstructions(0).also { assertRegisterIs(9, 9) } - removeInstructions(1).also { assertRegisterIs(1, 0) } - removeInstructions(2).also { assertRegisterIs(3, 0) } - } - - @Test - fun replaceInstructionsInImplementationIndexed() = applyToImplementation { - replaceInstructions(5, getTestInstructions(0..1)).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - assertRegisterIs(7, 7) + fun removeInstructionsFromImplementationIndexed() = + applyToImplementation { + removeInstructions(5, 5).also { assertRegisterIs(4, 4) } } - } @Test - fun addInstructionToMethodIndexed() = applyToMethod { - addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) } - } - - @Test - fun addInstructionToMethod() = applyToMethod { - addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) } - } - - @Test - fun addSmaliInstructionToMethodIndexed() = applyToMethod { - addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) } - } - - @Test - fun addSmaliInstructionToMethod() = applyToMethod { - addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) } - } - - @Test - fun addInstructionsToMethodIndexed() = applyToMethod { - addInstructions(5, getTestInstructions(0..1)).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - - assertRegisterIs(5, 7) + fun removeInstructionsFromImplementation() = + applyToImplementation { + removeInstructions(0).also { assertRegisterIs(9, 9) } + removeInstructions(1).also { assertRegisterIs(1, 0) } + removeInstructions(2).also { assertRegisterIs(3, 0) } } - } @Test - fun addInstructionsToMethod() = applyToMethod { - addInstructions(getTestInstructions(0..1)).also { - assertRegisterIs(0, 10) - assertRegisterIs(1, 11) - - assertRegisterIs(9, 9) + fun replaceInstructionsInImplementationIndexed() = + applyToImplementation { + replaceInstructions(5, getTestInstructions(0..1)).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) + assertRegisterIs(7, 7) + } } - } @Test - fun addSmaliInstructionsToMethodIndexed() = applyToMethod { - addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - - assertRegisterIs(5, 7) + fun addInstructionToMethodIndexed() = + applyToMethod { + addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) } } - } @Test - fun addSmaliInstructionsToMethod() = applyToMethod { - addInstructions(getTestSmaliInstructions(0..1)).also { - assertRegisterIs(0, 10) - assertRegisterIs(1, 11) - - assertRegisterIs(9, 9) + fun addInstructionToMethod() = + applyToMethod { + addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) } } - } @Test - fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod { - val label = ExternalLabel("testLabel", getInstruction(5)) - - addInstructionsWithLabels( - 5, - getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"), - label - ).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - assertRegisterIs(5, 8) - - val gotoTarget = getInstruction(7) - .target.location.instruction as OneRegisterInstruction - - assertEquals(5, gotoTarget.registerA) + fun addSmaliInstructionToMethodIndexed() = + applyToMethod { + addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) } } - } @Test - fun removeInstructionFromMethodIndexed() = applyToMethod { - removeInstruction(5).also { - assertRegisterIs(4, 4) - assertRegisterIs(6, 5) + fun addSmaliInstructionToMethod() = + applyToMethod { + addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) } } - } @Test - fun removeInstructionsFromMethodIndexed() = applyToMethod { - removeInstructions(5, 5).also { assertRegisterIs(4, 4) } - } + fun addInstructionsToMethodIndexed() = + applyToMethod { + addInstructions(5, getTestInstructions(0..1)).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) - @Test - fun removeInstructionsFromMethod() = applyToMethod { - removeInstructions(0).also { assertRegisterIs(9, 9) } - removeInstructions(1).also { assertRegisterIs(1, 0) } - removeInstructions(2).also { assertRegisterIs(3, 0) } - } - - @Test - fun replaceInstructionInMethodIndexed() = applyToMethod { - replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) } - } - - @Test - fun replaceInstructionsInMethodIndexed() = applyToMethod { - replaceInstructions(5, getTestInstructions(0..1)).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - assertRegisterIs(7, 7) + assertRegisterIs(5, 7) + } } - } @Test - fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod { - replaceInstructions(5, getTestSmaliInstructions(0..1)).also { - assertRegisterIs(0, 5) - assertRegisterIs(1, 6) - assertRegisterIs(7, 7) + fun addInstructionsToMethod() = + applyToMethod { + addInstructions(getTestInstructions(0..1)).also { + assertRegisterIs(0, 10) + assertRegisterIs(1, 11) + + assertRegisterIs(9, 9) + } + } + + @Test + fun addSmaliInstructionsToMethodIndexed() = + applyToMethod { + addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) + + assertRegisterIs(5, 7) + } + } + + @Test + fun addSmaliInstructionsToMethod() = + applyToMethod { + addInstructions(getTestSmaliInstructions(0..1)).also { + assertRegisterIs(0, 10) + assertRegisterIs(1, 11) + + assertRegisterIs(9, 9) + } + } + + @Test + fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = + applyToMethod { + val label = ExternalLabel("testLabel", getInstruction(5)) + + addInstructionsWithLabels( + 5, + getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"), + label, + ).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) + assertRegisterIs(5, 8) + + val gotoTarget = + getInstruction(7) + .target.location.instruction as OneRegisterInstruction + + assertEquals(5, gotoTarget.registerA) + } + } + + @Test + fun removeInstructionFromMethodIndexed() = + applyToMethod { + removeInstruction(5).also { + assertRegisterIs(4, 4) + assertRegisterIs(6, 5) + } + } + + @Test + fun removeInstructionsFromMethodIndexed() = + applyToMethod { + removeInstructions(5, 5).also { assertRegisterIs(4, 4) } + } + + @Test + fun removeInstructionsFromMethod() = + applyToMethod { + removeInstructions(0).also { assertRegisterIs(9, 9) } + removeInstructions(1).also { assertRegisterIs(1, 0) } + removeInstructions(2).also { assertRegisterIs(3, 0) } + } + + @Test + fun replaceInstructionInMethodIndexed() = + applyToMethod { + replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) } + } + + @Test + fun replaceInstructionsInMethodIndexed() = + applyToMethod { + replaceInstructions(5, getTestInstructions(0..1)).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) + assertRegisterIs(7, 7) + } + } + + @Test + fun replaceSmaliInstructionsInMethodIndexed() = + applyToMethod { + replaceInstructions(5, getTestSmaliInstructions(0..1)).also { + assertRegisterIs(0, 5) + assertRegisterIs(1, 6) + assertRegisterIs(7, 7) + } } - } // region Helper methods @@ -212,22 +234,29 @@ private object InstructionExtensionsTest { testMethod.apply(block) } - private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals( - register, getInstruction(atIndex).registerA + private fun MutableMethodImplementation.assertRegisterIs( + register: Int, + atIndex: Int, + ) = assertEquals( + register, + getInstruction(atIndex).registerA, ) - private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) = - implementation!!.assertRegisterIs(register, atIndex) + private fun MutableMethod.assertRegisterIs( + register: Int, + atIndex: Int, + ) = implementation!!.assertRegisterIs(register, atIndex) private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) } private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0" - private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") { - getTestSmaliInstruction(it) - } + private fun getTestSmaliInstructions(range: IntRange) = + range.joinToString("\n") { + getTestSmaliInstruction(it) + } // endregion private class TestInstruction(register: Int) : BuilderInstruction21s(Opcode.CONST_16, register, 0) -} \ No newline at end of file +} diff --git a/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt b/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt index 943d91d..d7fccb1 100644 --- a/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt +++ b/src/test/kotlin/app/revanced/patcher/patch/options/PatchOptionsTest.kt @@ -67,8 +67,7 @@ internal class PatchOptionsTest { } @Test - fun `should allow setting custom value`() = - assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = "unknown" } + fun `should allow setting custom value`() = assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = "unknown" } @Test fun `should allow resetting value`() = assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = null } @@ -86,42 +85,42 @@ internal class PatchOptionsTest { } @Test - fun `option types should be known`() = - assertTrue(OptionsTestPatch.options["array"].valueType == "StringArray") + fun `option types should be known`() = assertTrue(OptionsTestPatch.options["array"].valueType == "StringArray") @Test - fun `getting default value should work`() = - assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) } + fun `getting default value should work`() = assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) } private object OptionsTestPatch : BytecodePatch() { var booleanOption by booleanPatchOption( "bool", - true + true, ) var requiredStringOption by stringPatchOption( "required", "default", - required = true - ) - var stringArrayOption = stringArrayPatchOption( - "array", - arrayOf("1", "2") + required = true, ) + var stringArrayOption = + stringArrayPatchOption( + "array", + arrayOf("1", "2"), + ) var stringOptionWithChoices by stringPatchOption( "choices", "value", - values = mapOf("Valid option value" to "valid") + values = mapOf("Valid option value" to "valid"), ) var validatedOption by stringPatchOption( - "validated", - "default" + "default", ) { it == "valid" } - var resettableOption = stringPatchOption( - "resettable", null, - required = true - ) + var resettableOption = + stringPatchOption( + "resettable", + null, + required = true, + ) override fun execute(context: BytecodeContext) {} } -} \ No newline at end of file +} diff --git a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleBytecodePatch.kt b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleBytecodePatch.kt index cca07ed..2f4b5f2 100644 --- a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleBytecodePatch.kt +++ b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleBytecodePatch.kt @@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableList name = "Example bytecode patch", description = "Example demonstration of a bytecode patch.", dependencies = [ExampleResourcePatch::class], - compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))] + compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))], ) object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { // Entry point of a patch. Supplied fingerprints are resolved at this point. @@ -60,7 +60,7 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { invoke-static { }, LTestClass;->returnHello()Ljava/lang/String; move-result-object v1 invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V - """ + """, ) } @@ -82,14 +82,14 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { BuilderInstruction21c( Opcode.CONST_STRING, 0, - ImmutableStringReference("Hello, ReVanced! Adding bytecode.") + ImmutableStringReference("Hello, ReVanced! Adding bytecode."), ), - BuilderInstruction11x(Opcode.RETURN_OBJECT, 0) + BuilderInstruction11x(Opcode.RETURN_OBJECT, 0), ), null, - null - ) - ).toMutable() + null, + ), + ).toMutable(), ) // Add a field in the main class. @@ -105,12 +105,12 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { ImmutableFieldReference( "Ljava/lang/System;", "out", - "Ljava/io/PrintStream;" - ) + "Ljava/io/PrintStream;", + ), ), null, - null - ).toMutable() + null, + ).toMutable(), ) } } ?: throw PatchException("Fingerprint failed to resolve.") @@ -121,7 +121,10 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { * @param index The index of the instruction to replace. * @param string The replacement string. */ - private fun MutableMethod.replaceStringAt(index: Int, string: String) { + private fun MutableMethod.replaceStringAt( + index: Int, + string: String, + ) { val instruction = getInstruction(index) // Utility method of dexlib2. @@ -139,8 +142,7 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) { // At last, use the method replaceInstruction to replace it at the given index startIndex. replaceInstruction( index, - "const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}" + "const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}", ) } } - diff --git a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleFingerprint.kt b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleFingerprint.kt index 3a1d7d4..64bd62d 100644 --- a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleFingerprint.kt +++ b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleFingerprint.kt @@ -1,7 +1,7 @@ package app.revanced.patcher.patch.usage import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -12,9 +12,9 @@ object ExampleFingerprint : MethodFingerprint( listOf("[L"), listOf( Opcode.SGET_OBJECT, - null, // Matching unknown opcodes. + null, // Matching unknown opcodes. Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching. - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ), - null -) \ No newline at end of file + null, +) diff --git a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleResourcePatch.kt b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleResourcePatch.kt index f7a9981..011c0f0 100644 --- a/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleResourcePatch.kt +++ b/src/test/kotlin/app/revanced/patcher/patch/usage/ExampleResourcePatch.kt @@ -4,19 +4,19 @@ import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.ResourcePatch import org.w3c.dom.Element - class ExampleResourcePatch : ResourcePatch() { override fun execute(context: ResourceContext) { context.xmlEditor["AndroidManifest.xml"].use { editor -> - val element = editor // regular DomFileEditor - .file - .getElementsByTagName("application") - .item(0) as Element + val element = + editor // regular DomFileEditor + .file + .getElementsByTagName("application") + .item(0) as Element element .setAttribute( "exampleAttribute", - "exampleValue" + "exampleValue", ) } } -} \ No newline at end of file +} diff --git a/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt b/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt index 08b0393..4421932 100644 --- a/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt +++ b/src/test/kotlin/app/revanced/patcher/util/smali/InlineSmaliCompilerTest.kt @@ -33,16 +33,18 @@ internal class InlineSmaliCompilerTest { val insnIndex = insnAmount - 2 val targetIndex = insnIndex - 1 - method.addInstructions(arrayOfNulls(insnAmount).also { - Arrays.fill(it, "const/4 v0, 0x0") - }.joinToString("\n")) + method.addInstructions( + arrayOfNulls(insnAmount).also { + Arrays.fill(it, "const/4 v0, 0x0") + }.joinToString("\n"), + ) method.addInstructionsWithLabels( targetIndex, """ :test const/4 v0, 0x1 if-eqz v0, :test - """ + """, ) val insn = method.getInstruction(insnIndex) @@ -59,7 +61,7 @@ internal class InlineSmaliCompilerTest { """ const/4 v0, 0x1 const/4 v0, 0x0 - """ + """, ) assertEquals(labelIndex, method.newLabel(labelIndex).location.index) @@ -71,7 +73,7 @@ internal class InlineSmaliCompilerTest { if-eqz v0, :test return-void """, - ExternalLabel("test", method.getInstruction(1)) + ExternalLabel("test", method.getInstruction(1)), ) val insn = method.getInstruction(insnIndex) @@ -93,13 +95,16 @@ internal class InlineSmaliCompilerTest { accessFlags, emptySet(), emptySet(), - MutableMethodImplementation(registerCount) + MutableMethodImplementation(registerCount), ).toMutable() - private fun instructionEquals(want: BuilderInstruction, have: BuilderInstruction) { + private fun instructionEquals( + want: BuilderInstruction, + have: BuilderInstruction, + ) { assertEquals(want.opcode, have.opcode) assertEquals(want.format, have.format) assertEquals(want.codeUnits, have.codeUnits) } } -} \ No newline at end of file +}