mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-04-30 05:14:26 +02:00
chore: Lint code
This commit is contained in:
parent
287841d806
commit
80407b6102
@ -37,7 +37,7 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
// This constructor parameter is unfortunately necessary,
|
// 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.
|
// 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.
|
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
||||||
private val patchSet: MutableSet<Patch<*>> = mutableSetOf()
|
private val patchSet: MutableSet<Patch<*>> = mutableSetOf(),
|
||||||
) : PatchSet by patchSet {
|
) : PatchSet by patchSet {
|
||||||
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||||
|
|
||||||
@ -63,22 +63,29 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
* @param silent Whether to suppress logging.
|
* @param silent Whether to suppress logging.
|
||||||
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
* @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 {
|
return try {
|
||||||
getField("INSTANCE").get(null)
|
getField("INSTANCE").get(null)
|
||||||
} catch (exception: NoSuchFieldException) {
|
} catch (exception: NoSuchFieldException) {
|
||||||
if (!silent) logger.fine(
|
if (!silent) {
|
||||||
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
logger.fine(
|
||||||
"Will try to instantiate it."
|
"Patch class '$name' has no INSTANCE field, therefor not a singleton. " +
|
||||||
|
"Will try to instantiate it.",
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
getDeclaredConstructor().newInstance()
|
getDeclaredConstructor().newInstance()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
if (!silent) logger.severe(
|
if (!silent) {
|
||||||
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
logger.severe(
|
||||||
"therefor cannot be instantiated and will be ignored."
|
"Patch class '$name' is not singleton and has no suitable constructor, " +
|
||||||
|
"therefor cannot be instantiated and will be ignored.",
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -97,7 +104,7 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
{ patchBundle ->
|
{ patchBundle ->
|
||||||
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
||||||
.map { it.name.replace('/', '.').replace(".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(
|
class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader(
|
||||||
DexClassLoader(
|
DexClassLoader(
|
||||||
patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, optimizedDexDirectory?.absolutePath,
|
patchBundles.joinToString(File.pathSeparator) { it.absolutePath },
|
||||||
|
optimizedDexDirectory?.absolutePath,
|
||||||
null,
|
null,
|
||||||
PatchBundleLoader::class.java.classLoader
|
PatchBundleLoader::class.java.classLoader,
|
||||||
),
|
),
|
||||||
patchBundles,
|
patchBundles,
|
||||||
{ patchBundle ->
|
{ patchBundle ->
|
||||||
@ -119,7 +127,7 @@ sealed class PatchBundleLoader private constructor(
|
|||||||
.map { classDef ->
|
.map { classDef ->
|
||||||
classDef.type.substring(1, classDef.length - 1)
|
classDef.type.substring(1, classDef.length - 1)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
||||||
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
||||||
|
@ -17,7 +17,7 @@ import java.util.logging.Logger
|
|||||||
* @param options The options for the patcher.
|
* @param options The options for the patcher.
|
||||||
*/
|
*/
|
||||||
class Patcher(
|
class Patcher(
|
||||||
private val options: PatcherOptions
|
private val options: PatcherOptions,
|
||||||
) : PatchExecutorFunction, PatchesConsumer, IntegrationsConsumer, Supplier<PatcherResult>, Closeable {
|
) : PatchExecutorFunction, PatchesConsumer, IntegrationsConsumer, Supplier<PatcherResult>, Closeable {
|
||||||
private val logger = Logger.getLogger(Patcher::class.java.name)
|
private val logger = Logger.getLogger(Patcher::class.java.name)
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ class Patcher(
|
|||||||
// * @param patches The [Patch]es to add.
|
// * @param patches The [Patch]es to add.
|
||||||
// * @throws PatcherException.CircularDependencyException If a circular dependency is detected.
|
// * @throws PatcherException.CircularDependencyException If a circular dependency is detected.
|
||||||
// */
|
// */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add [Patch]es to ReVanced [Patcher].
|
* Add [Patch]es to ReVanced [Patcher].
|
||||||
*
|
*
|
||||||
@ -126,8 +127,8 @@ class Patcher(
|
|||||||
* @param returnOnError If true, ReVanced [Patcher] will return immediately if a [Patch] fails.
|
* @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].
|
* @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.
|
* Execute a [Patch] and its dependencies recursively.
|
||||||
*
|
*
|
||||||
@ -137,7 +138,7 @@ class Patcher(
|
|||||||
*/
|
*/
|
||||||
fun executePatch(
|
fun executePatch(
|
||||||
patch: Patch<*>,
|
patch: Patch<*>,
|
||||||
executedPatches: LinkedHashMap<Patch<*>, PatchResult>
|
executedPatches: LinkedHashMap<Patch<*>, PatchResult>,
|
||||||
): PatchResult {
|
): PatchResult {
|
||||||
val patchName = patch.name ?: patch.toString()
|
val patchName = patch.name ?: patch.toString()
|
||||||
|
|
||||||
@ -159,8 +160,8 @@ class Patcher(
|
|||||||
patch,
|
patch,
|
||||||
PatchException(
|
PatchException(
|
||||||
"'$patchName' depends on '${dependency.name ?: dependency}' " +
|
"'$patchName' depends on '${dependency.name ?: dependency}' " +
|
||||||
"that raised an exception:\n${it.stackTraceToString()}"
|
"that raised an exception:\n${it.stackTraceToString()}",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,8 +191,9 @@ class Patcher(
|
|||||||
LookupMap.initializeLookupMaps(context.bytecodeContext)
|
LookupMap.initializeLookupMaps(context.bytecodeContext)
|
||||||
|
|
||||||
// Prevent from decoding the app manifest twice if it is not needed.
|
// Prevent from decoding the app manifest twice if it is not needed.
|
||||||
if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL)
|
if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL) {
|
||||||
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL)
|
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL)
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("Executing patches")
|
logger.info("Executing patches")
|
||||||
|
|
||||||
@ -219,7 +221,8 @@ class Patcher(
|
|||||||
.filter { it.patch is Closeable }.asReversed().forEach { executedPatch ->
|
.filter { it.patch is Closeable }.asReversed().forEach { executedPatch ->
|
||||||
val patch = executedPatch.patch
|
val patch = executedPatch.patch
|
||||||
|
|
||||||
val result = try {
|
val result =
|
||||||
|
try {
|
||||||
(patch as Closeable).close()
|
(patch as Closeable).close()
|
||||||
|
|
||||||
executedPatch
|
executedPatch
|
||||||
@ -235,9 +238,9 @@ class Patcher(
|
|||||||
patch,
|
patch,
|
||||||
PatchException(
|
PatchException(
|
||||||
"'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}",
|
"'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}",
|
||||||
result.exception
|
result.exception,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (returnOnError) return@flow
|
if (returnOnError) return@flow
|
||||||
@ -256,10 +259,10 @@ class Patcher(
|
|||||||
*
|
*
|
||||||
* @return The [PatcherResult] containing the patched input files.
|
* @return The [PatcherResult] containing the patched input files.
|
||||||
*/
|
*/
|
||||||
override fun get() = PatcherResult(
|
override fun get() =
|
||||||
|
PatcherResult(
|
||||||
context.bytecodeContext.get(),
|
context.bytecodeContext.get(),
|
||||||
context.resourceContext.get(),
|
context.resourceContext.get(),
|
||||||
context.packageMetadata.apkInfo.doNotCompress?.toList()
|
context.packageMetadata.apkInfo.doNotCompress?.toList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,7 @@ package app.revanced.patcher
|
|||||||
sealed class PatcherException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
sealed class PatcherException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
||||||
constructor(errorMessage: String) : this(errorMessage, null)
|
constructor(errorMessage: String) : this(errorMessage, null)
|
||||||
|
|
||||||
|
|
||||||
class CircularDependencyException internal constructor(dependant: String) : PatcherException(
|
class CircularDependencyException internal constructor(dependant: String) : PatcherException(
|
||||||
"Patch '$dependant' causes a circular dependency"
|
"Patch '$dependant' causes a circular dependency",
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -32,19 +32,22 @@ data class PatcherOptions(
|
|||||||
/**
|
/**
|
||||||
* The configuration to use for resource decoding and compiling.
|
* The configuration to use for resource decoding and compiling.
|
||||||
*/
|
*/
|
||||||
internal val resourceConfig = Config.getDefaultConfig().apply {
|
internal val resourceConfig =
|
||||||
|
Config.getDefaultConfig().apply {
|
||||||
useAapt2 = true
|
useAapt2 = true
|
||||||
aaptPath = aaptBinaryPath ?: ""
|
aaptPath = aaptBinaryPath ?: ""
|
||||||
frameworkDirectory = frameworkFileDirectory
|
frameworkDirectory = frameworkFileDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recreateResourceCacheDirectory() = resourceCachePath.also {
|
fun recreateResourceCacheDirectory() =
|
||||||
|
resourceCachePath.also {
|
||||||
if (it.exists()) {
|
if (it.exists()) {
|
||||||
logger.info("Deleting existing resource cache directory")
|
logger.info("Deleting existing resource cache directory")
|
||||||
|
|
||||||
if (!it.deleteRecursively())
|
if (!it.deleteRecursively()) {
|
||||||
logger.severe("Failed to delete existing resource cache directory")
|
logger.severe("Failed to delete existing resource cache directory")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
it.mkdirs()
|
it.mkdirs()
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import java.io.InputStream
|
|||||||
data class PatcherResult(
|
data class PatcherResult(
|
||||||
val dexFiles: List<PatchedDexFile>,
|
val dexFiles: List<PatchedDexFile>,
|
||||||
val resourceFile: File?,
|
val resourceFile: File?,
|
||||||
val doNotCompress: List<String>? = null
|
val doNotCompress: List<String>? = null,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Wrapper for dex files.
|
* Wrapper for dex files.
|
||||||
|
@ -41,8 +41,12 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
val classes by lazy {
|
val classes by lazy {
|
||||||
ProxyClassList(
|
ProxyClassList(
|
||||||
MultiDexIO.readDexFile(
|
MultiDexIO.readDexFile(
|
||||||
true, options.inputFile, BasicDexFileNamer(), null, null
|
true,
|
||||||
).also { opcodes = it.opcodes }.classes.toMutableSet()
|
options.inputFile,
|
||||||
|
BasicDexFileNamer(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
).also { opcodes = it.opcodes }.classes.toMutableSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +71,8 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
*/
|
*/
|
||||||
fun findClass(predicate: (ClassDef) -> Boolean) =
|
fun findClass(predicate: (ClassDef) -> Boolean) =
|
||||||
// if we already proxied the class matching the predicate...
|
// if we already proxied the class matching the predicate...
|
||||||
classes.proxies.firstOrNull { predicate(it.immutableClass) } ?:
|
classes.proxies.firstOrNull { predicate(it.immutableClass) }
|
||||||
// else resolve the class to a proxy and return it, if the predicate is matching a class
|
?: // else resolve the class to a proxy and return it, if the predicate is matching a class
|
||||||
classes.find(predicate)?.let { proxy(it) }
|
classes.find(predicate)?.let { proxy(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +82,8 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
* @param classDef The class to proxy.
|
* @param classDef The class to proxy.
|
||||||
* @return A proxy for the class.
|
* @return A proxy for the class.
|
||||||
*/
|
*/
|
||||||
fun proxy(classDef: ClassDef) = this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let {
|
fun proxy(classDef: ClassDef) =
|
||||||
|
this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let {
|
||||||
ClassProxy(classDef).also { this.classes.add(it) }
|
ClassProxy(classDef).also { this.classes.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +103,8 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
override fun get(): List<PatcherResult.PatchedDexFile> {
|
override fun get(): List<PatcherResult.PatchedDexFile> {
|
||||||
logger.info("Compiling patched dex files")
|
logger.info("Compiling patched dex files")
|
||||||
|
|
||||||
val patchedDexFileResults = options.resourceCachePath.resolve("dex").also {
|
val patchedDexFileResults =
|
||||||
|
options.resourceCachePath.resolve("dex").also {
|
||||||
it.deleteRecursively() // Make sure the directory is empty.
|
it.deleteRecursively() // Make sure the directory is empty.
|
||||||
it.mkdirs()
|
it.mkdirs()
|
||||||
}.apply {
|
}.apply {
|
||||||
@ -109,9 +115,10 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
BasicDexFileNamer(),
|
BasicDexFileNamer(),
|
||||||
object : DexFile {
|
object : DexFile {
|
||||||
override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses)
|
override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses)
|
||||||
|
|
||||||
override fun getOpcodes() = this@BytecodeContext.opcodes
|
override fun getOpcodes() = this@BytecodeContext.opcodes
|
||||||
},
|
},
|
||||||
DexIO.DEFAULT_MAX_DEX_POOL_SIZE
|
DexIO.DEFAULT_MAX_DEX_POOL_SIZE,
|
||||||
) { _, entryName, _ -> logger.info("Compiled $entryName") }
|
) { _, entryName, _ -> logger.info("Compiled $entryName") }
|
||||||
}.listFiles(FileFilter { it.isFile })!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) }
|
}.listFiles(FileFilter { it.isFile })!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) }
|
||||||
|
|
||||||
@ -143,11 +150,13 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
this@Integrations.forEach { integrations ->
|
this@Integrations.forEach { integrations ->
|
||||||
MultiDexIO.readDexFile(
|
MultiDexIO.readDexFile(
|
||||||
true,
|
true,
|
||||||
integrations, BasicDexFileNamer(),
|
integrations,
|
||||||
|
BasicDexFileNamer(),
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
|
||||||
).classes.forEach classDef@{ classDef ->
|
).classes.forEach classDef@{ classDef ->
|
||||||
val existingClass = classMap[classDef.type] ?: run {
|
val existingClass =
|
||||||
|
classMap[classDef.type] ?: run {
|
||||||
logger.fine("Adding $classDef")
|
logger.fine("Adding $classDef")
|
||||||
classes.add(classDef)
|
classes.add(classDef)
|
||||||
return@classDef
|
return@classDef
|
||||||
@ -158,7 +167,10 @@ class BytecodeContext internal constructor(private val options: PatcherOptions)
|
|||||||
existingClass.merge(classDef, this@BytecodeContext).let { mergedClass ->
|
existingClass.merge(classDef, this@BytecodeContext).let { mergedClass ->
|
||||||
// If the class was merged, replace the original class with the merged class.
|
// If the class was merged, replace the original class with the merged class.
|
||||||
if (mergedClass === existingClass) return@let
|
if (mergedClass === existingClass) return@let
|
||||||
classes.apply { remove(existingClass); add(mergedClass) }
|
classes.apply {
|
||||||
|
remove(existingClass)
|
||||||
|
add(mergedClass)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import java.util.logging.Logger
|
|||||||
*/
|
*/
|
||||||
class ResourceContext internal constructor(
|
class ResourceContext internal constructor(
|
||||||
private val context: PatcherContext,
|
private val context: PatcherContext,
|
||||||
private val options: PatcherOptions
|
private val options: PatcherOptions,
|
||||||
) : Context<File?>, Iterable<File> {
|
) : Context<File?>, Iterable<File> {
|
||||||
private val logger = Logger.getLogger(ResourceContext::class.java.name)
|
private val logger = Logger.getLogger(ResourceContext::class.java.name)
|
||||||
|
|
||||||
@ -37,7 +37,8 @@ class ResourceContext internal constructor(
|
|||||||
*
|
*
|
||||||
* @param mode The [ResourceDecodingMode] to use when decoding.
|
* @param mode The [ResourceDecodingMode] to use when decoding.
|
||||||
*/
|
*/
|
||||||
internal fun decodeResources(mode: ResourceDecodingMode) = with(context.packageMetadata.apkInfo) {
|
internal fun decodeResources(mode: ResourceDecodingMode) =
|
||||||
|
with(context.packageMetadata.apkInfo) {
|
||||||
// Needed to decode resources.
|
// Needed to decode resources.
|
||||||
val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this)
|
val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this)
|
||||||
|
|
||||||
@ -54,7 +55,8 @@ class ResourceContext internal constructor(
|
|||||||
val apkDecoder = ApkDecoder(options.resourceConfig, this)
|
val apkDecoder = ApkDecoder(options.resourceConfig, this)
|
||||||
apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping)
|
apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping)
|
||||||
|
|
||||||
usesFramework = UsesFramework().apply {
|
usesFramework =
|
||||||
|
UsesFramework().apply {
|
||||||
ids = resourcesDecoder.resTable.listFramePackages().map { it.id }
|
ids = resourcesDecoder.resTable.listFramePackages().map { it.id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,14 +68,14 @@ class ResourceContext internal constructor(
|
|||||||
// because it does not support decoding to an OutputStream.
|
// because it does not support decoding to an OutputStream.
|
||||||
XmlPullStreamDecoder(
|
XmlPullStreamDecoder(
|
||||||
AndroidManifestResourceParser(resourcesDecoder.resTable),
|
AndroidManifestResourceParser(resourcesDecoder.resTable),
|
||||||
resourcesDecoder.resXmlSerializer
|
resourcesDecoder.resXmlSerializer,
|
||||||
).decodeManifest(
|
).decodeManifest(
|
||||||
apkFile.directory.getFileInput("AndroidManifest.xml"),
|
apkFile.directory.getFileInput("AndroidManifest.xml"),
|
||||||
// Older Android versions do not support OutputStream.nullOutputStream()
|
// Older Android versions do not support OutputStream.nullOutputStream()
|
||||||
object : OutputStream() {
|
object : OutputStream() {
|
||||||
override fun write(b: Int) { /* do nothing */
|
override fun write(b: Int) { // do nothing
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get the package name and version from the manifest using the XmlPullStreamDecoder.
|
// Get the package name and version from the manifest using the XmlPullStreamDecoder.
|
||||||
@ -96,14 +98,12 @@ class ResourceContext internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(path: String) = options.resourceCachePath.resolve(path)
|
operator fun get(path: String) = options.resourceCachePath.resolve(path)
|
||||||
|
|
||||||
override fun iterator() = options.resourceCachePath.walkTopDown().iterator()
|
override fun iterator() = options.resourceCachePath.walkTopDown().iterator()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile resources from the [ResourceContext].
|
* Compile resources from the [ResourceContext].
|
||||||
*
|
*
|
||||||
@ -116,14 +116,17 @@ class ResourceContext internal constructor(
|
|||||||
logger.info("Compiling modified resources")
|
logger.info("Compiling modified resources")
|
||||||
|
|
||||||
val cacheDirectory = ExtFile(options.resourceCachePath)
|
val cacheDirectory = ExtFile(options.resourceCachePath)
|
||||||
val aaptFile = cacheDirectory.resolve("aapt_temp_file").also {
|
val aaptFile =
|
||||||
|
cacheDirectory.resolve("aapt_temp_file").also {
|
||||||
Files.deleteIfExists(it.toPath())
|
Files.deleteIfExists(it.toPath())
|
||||||
}.also { resourceFile = it }
|
}.also { resourceFile = it }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AaptInvoker(
|
AaptInvoker(
|
||||||
options.resourceConfig, context.packageMetadata.apkInfo
|
options.resourceConfig,
|
||||||
).invokeAapt(aaptFile,
|
context.packageMetadata.apkInfo,
|
||||||
|
).invokeAapt(
|
||||||
|
aaptFile,
|
||||||
cacheDirectory.resolve("AndroidManifest.xml").also {
|
cacheDirectory.resolve("AndroidManifest.xml").also {
|
||||||
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
|
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
|
||||||
},
|
},
|
||||||
@ -134,7 +137,8 @@ class ResourceContext internal constructor(
|
|||||||
usesFramework.ids.map { id ->
|
usesFramework.ids.map { id ->
|
||||||
Framework(options.resourceConfig).getFrameworkApk(id, usesFramework.tag)
|
Framework(options.resourceConfig).getFrameworkApk(id, usesFramework.tag)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
})
|
},
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
cacheDirectory.close()
|
cacheDirectory.close()
|
||||||
}
|
}
|
||||||
@ -159,12 +163,10 @@ class ResourceContext internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class XmlFileHolder {
|
inner class XmlFileHolder {
|
||||||
operator fun get(inputStream: InputStream) =
|
operator fun get(inputStream: InputStream) = DomFileEditor(inputStream)
|
||||||
DomFileEditor(inputStream)
|
|
||||||
|
|
||||||
operator fun get(path: String): DomFileEditor {
|
operator fun get(path: String): DomFileEditor {
|
||||||
return DomFileEditor(this@ResourceContext[path])
|
return DomFileEditor(this@ResourceContext[path])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,11 +11,13 @@ internal object AnnotationExtensions {
|
|||||||
*/
|
*/
|
||||||
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
|
||||||
fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
||||||
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
targetAnnotation: Class<T>,
|
||||||
|
traversed: MutableSet<Annotation>,
|
||||||
): T? {
|
): T? {
|
||||||
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
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) {
|
for (annotation in this.annotations) {
|
||||||
if (traversed.contains(annotation)) continue
|
if (traversed.contains(annotation)) continue
|
||||||
|
@ -12,7 +12,6 @@ import com.android.tools.smali.dexlib2.builder.instruction.*
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
object InstructionExtensions {
|
object InstructionExtensions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* Add instructions to a method at the given index.
|
||||||
*
|
*
|
||||||
@ -21,7 +20,7 @@ object InstructionExtensions {
|
|||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.addInstructions(
|
fun MutableMethodImplementation.addInstructions(
|
||||||
index: Int,
|
index: Int,
|
||||||
instructions: List<BuilderInstruction>
|
instructions: List<BuilderInstruction>,
|
||||||
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +38,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to remove the instructions at.
|
* @param index The index to remove the instructions at.
|
||||||
* @param count The amount of instructions to remove.
|
* @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)
|
removeInstruction(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +59,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param instructions The instructions to replace the instructions with.
|
* @param instructions The instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
fun MutableMethodImplementation.replaceInstructions(
|
||||||
|
index: Int,
|
||||||
|
instructions: List<BuilderInstruction>,
|
||||||
|
) {
|
||||||
// Remove the instructions at the given index.
|
// Remove the instructions at the given index.
|
||||||
removeInstructions(index, instructions.size)
|
removeInstructions(index, instructions.size)
|
||||||
|
|
||||||
@ -71,16 +76,17 @@ object InstructionExtensions {
|
|||||||
* @param index The index to add the instruction at.
|
* @param index The index to add the instruction at.
|
||||||
* @param instruction The instruction to add.
|
* @param instruction The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(index: Int, instruction: BuilderInstruction) =
|
fun MutableMethod.addInstruction(
|
||||||
implementation!!.addInstruction(index, instruction)
|
index: Int,
|
||||||
|
instruction: BuilderInstruction,
|
||||||
|
) = implementation!!.addInstruction(index, instruction)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method.
|
* Add an instruction to a method.
|
||||||
*
|
*
|
||||||
* @param instruction The instructions to add.
|
* @param instruction The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(instruction: BuilderInstruction) =
|
fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction)
|
||||||
implementation!!.addInstruction(instruction)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method at the given index.
|
* 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 index The index to add the instruction at.
|
||||||
* @param smaliInstructions The instruction to add.
|
* @param smaliInstructions The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(index: Int, smaliInstructions: String) =
|
fun MutableMethod.addInstruction(
|
||||||
implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
index: Int,
|
||||||
|
smaliInstructions: String,
|
||||||
|
) = implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method.
|
* Add an instruction to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instruction to add.
|
* @param smaliInstructions The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(smaliInstructions: String) =
|
fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
||||||
implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* 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 index The index to add the instructions at.
|
||||||
* @param instructions The instructions to add.
|
* @param instructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
fun MutableMethod.addInstructions(
|
||||||
implementation!!.addInstructions(index, instructions)
|
index: Int,
|
||||||
|
instructions: List<BuilderInstruction>,
|
||||||
|
) = implementation!!.addInstructions(index, instructions)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param instructions The instructions to add.
|
* @param instructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) =
|
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) = implementation!!.addInstructions(instructions)
|
||||||
implementation!!.addInstructions(instructions)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instructions to add.
|
* @param smaliInstructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) =
|
fun MutableMethod.addInstructions(
|
||||||
implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
index: Int,
|
||||||
|
smaliInstructions: String,
|
||||||
|
) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instructions to add.
|
* @param smaliInstructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(smaliInstructions: String) =
|
fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
||||||
implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* Add instructions to a method at the given index.
|
||||||
@ -144,10 +152,11 @@ object InstructionExtensions {
|
|||||||
fun MutableMethod.addInstructionsWithLabels(
|
fun MutableMethod.addInstructionsWithLabels(
|
||||||
index: Int,
|
index: Int,
|
||||||
smaliInstructions: String,
|
smaliInstructions: String,
|
||||||
vararg externalLabels: ExternalLabel
|
vararg externalLabels: ExternalLabel,
|
||||||
) {
|
) {
|
||||||
// Create reference dummy instructions for the instructions.
|
// Create reference dummy instructions for the instructions.
|
||||||
val nopSmali = StringBuilder(smaliInstructions).also { builder ->
|
val nopSmali =
|
||||||
|
StringBuilder(smaliInstructions).also { builder ->
|
||||||
externalLabels.forEach { (name, _) ->
|
externalLabels.forEach { (name, _) ->
|
||||||
builder.append("\n:$name\nnop")
|
builder.append("\n:$name\nnop")
|
||||||
}
|
}
|
||||||
@ -159,7 +168,7 @@ object InstructionExtensions {
|
|||||||
// Add the compiled list of instructions to the method.
|
// Add the compiled list of instructions to the method.
|
||||||
addInstructions(
|
addInstructions(
|
||||||
index,
|
index,
|
||||||
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size)
|
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size),
|
||||||
)
|
)
|
||||||
|
|
||||||
implementation!!.apply {
|
implementation!!.apply {
|
||||||
@ -174,22 +183,24 @@ object InstructionExtensions {
|
|||||||
*/
|
*/
|
||||||
fun Instruction.makeNewLabel() {
|
fun Instruction.makeNewLabel() {
|
||||||
fun replaceOffset(
|
fun replaceOffset(
|
||||||
i: BuilderOffsetInstruction, label: Label
|
i: BuilderOffsetInstruction,
|
||||||
|
label: Label,
|
||||||
): BuilderOffsetInstruction {
|
): BuilderOffsetInstruction {
|
||||||
return when (i) {
|
return when (i) {
|
||||||
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
||||||
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
||||||
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
||||||
is BuilderInstruction22t -> BuilderInstruction22t(
|
is BuilderInstruction22t ->
|
||||||
|
BuilderInstruction22t(
|
||||||
i.opcode,
|
i.opcode,
|
||||||
i.registerA,
|
i.registerA,
|
||||||
i.registerB,
|
i.registerB,
|
||||||
label
|
label,
|
||||||
)
|
)
|
||||||
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
||||||
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
||||||
else -> throw IllegalStateException(
|
else -> throw IllegalStateException(
|
||||||
"A non-offset instruction was given, this should never happen!"
|
"A non-offset instruction was given, this should never happen!",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,8 +209,10 @@ object InstructionExtensions {
|
|||||||
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
||||||
|
|
||||||
// Create the final instruction with the new label.
|
// Create the final instruction with the new label.
|
||||||
val newInstruction = replaceOffset(
|
val newInstruction =
|
||||||
compiledInstruction, label
|
replaceOffset(
|
||||||
|
compiledInstruction,
|
||||||
|
label,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Replace the instruction pointing to the dummy label
|
// Replace the instruction pointing to the dummy label
|
||||||
@ -233,8 +246,7 @@ object InstructionExtensions {
|
|||||||
*
|
*
|
||||||
* @param index The index to remove the instruction at.
|
* @param index The index to remove the instruction at.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstruction(index: Int) =
|
fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index)
|
||||||
implementation!!.removeInstruction(index)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instructions at the given index.
|
* Remove instructions at the given index.
|
||||||
@ -242,16 +254,17 @@ object InstructionExtensions {
|
|||||||
* @param index The index to remove the instructions at.
|
* @param index The index to remove the instructions at.
|
||||||
* @param count The amount of instructions to remove.
|
* @param count The amount of instructions to remove.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstructions(index: Int, count: Int) =
|
fun MutableMethod.removeInstructions(
|
||||||
implementation!!.removeInstructions(index, count)
|
index: Int,
|
||||||
|
count: Int,
|
||||||
|
) = implementation!!.removeInstructions(index, count)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instructions at the given index.
|
* Remove instructions at the given index.
|
||||||
*
|
*
|
||||||
* @param count The amount of instructions to remove.
|
* @param count The amount of instructions to remove.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstructions(count: Int) =
|
fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count)
|
||||||
implementation!!.removeInstructions(count)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace an instruction at the given index.
|
* Replace an instruction at the given index.
|
||||||
@ -259,8 +272,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instruction at.
|
* @param index The index to replace the instruction at.
|
||||||
* @param instruction The instruction to replace the instruction with.
|
* @param instruction The instruction to replace the instruction with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstruction(index: Int, instruction: BuilderInstruction) =
|
fun MutableMethod.replaceInstruction(
|
||||||
implementation!!.replaceInstruction(index, instruction)
|
index: Int,
|
||||||
|
instruction: BuilderInstruction,
|
||||||
|
) = implementation!!.replaceInstruction(index, instruction)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace an instruction at the given index.
|
* Replace an instruction at the given index.
|
||||||
@ -268,8 +283,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instruction at.
|
* @param index The index to replace the instruction at.
|
||||||
* @param smaliInstruction The smali instruction to replace the instruction with.
|
* @param smaliInstruction The smali instruction to replace the instruction with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstruction(index: Int, smaliInstruction: String) =
|
fun MutableMethod.replaceInstruction(
|
||||||
implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
index: Int,
|
||||||
|
smaliInstruction: String,
|
||||||
|
) = implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace instructions at the given index.
|
* Replace instructions at the given index.
|
||||||
@ -277,8 +294,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param instructions The instructions to replace the instructions with.
|
* @param instructions The instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
fun MutableMethod.replaceInstructions(
|
||||||
implementation!!.replaceInstructions(index, instructions)
|
index: Int,
|
||||||
|
instructions: List<BuilderInstruction>,
|
||||||
|
) = implementation!!.replaceInstructions(index, instructions)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace instructions at the given index.
|
* Replace instructions at the given index.
|
||||||
@ -286,8 +305,10 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param smaliInstructions The smali instructions to replace the instructions with.
|
* @param smaliInstructions The smali instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) =
|
fun MutableMethod.replaceInstructions(
|
||||||
implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
index: Int,
|
||||||
|
smaliInstructions: String,
|
||||||
|
) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index.
|
* Get an instruction at the given index.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
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.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
||||||
|
|
||||||
object MethodFingerprintExtensions {
|
object MethodFingerprintExtensions {
|
||||||
// TODO: Make this a property.
|
// TODO: Make this a property.
|
||||||
|
@ -20,7 +20,7 @@ internal class LookupMap : MutableMap<String, LookupMap.MethodClassList> by muta
|
|||||||
*/
|
*/
|
||||||
fun add(
|
fun add(
|
||||||
key: String,
|
key: String,
|
||||||
methodClassPair: MethodClassPair
|
methodClassPair: MethodClassPair,
|
||||||
) {
|
) {
|
||||||
getOrPut(key) { MethodClassList() }.add(methodClassPair)
|
getOrPut(key) { MethodClassList() }.add(methodClassPair)
|
||||||
}
|
}
|
||||||
@ -73,13 +73,14 @@ internal class LookupMap : MutableMap<String, LookupMap.MethodClassList> by muta
|
|||||||
append(accessFlagsReturnKey)
|
append(accessFlagsReturnKey)
|
||||||
appendParameters(method.parameterTypes)
|
appendParameters(method.parameterTypes)
|
||||||
},
|
},
|
||||||
methodClassPair
|
methodClassPair,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add strings contained in the method as the key.
|
// Add strings contained in the method as the key.
|
||||||
method.implementation?.instructions?.forEach instructions@{ instruction ->
|
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
|
return@instructions
|
||||||
|
}
|
||||||
|
|
||||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
|
|
||||||
@ -120,6 +121,5 @@ internal class LookupMap : MutableMap<String, LookupMap.MethodClassList> by muta
|
|||||||
append(parameter.first())
|
append(parameter.first())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ abstract class MethodFingerprint(
|
|||||||
internal val parameters: Iterable<String>? = null,
|
internal val parameters: Iterable<String>? = null,
|
||||||
internal val opcodes: Iterable<Opcode?>? = null,
|
internal val opcodes: Iterable<Opcode?>? = null,
|
||||||
internal val strings: Iterable<String>? = null,
|
internal val strings: Iterable<String>? = null,
|
||||||
internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null
|
internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* The result of the [MethodFingerprint].
|
* The result of the [MethodFingerprint].
|
||||||
@ -86,7 +86,8 @@ abstract class MethodFingerprint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val key = buildString {
|
val key =
|
||||||
|
buildString {
|
||||||
append(accessFlags)
|
append(accessFlags)
|
||||||
append(returnTypeValue.first())
|
append(returnTypeValue.first())
|
||||||
if (parameters != null) appendParameters(parameters)
|
if (parameters != null) appendParameters(parameters)
|
||||||
@ -107,7 +108,11 @@ abstract class MethodFingerprint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val methodsWithSameStrings = methodStringsLookup()
|
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).
|
// No strings declared or none matched (partial matches are allowed).
|
||||||
// Use signature matching.
|
// Use signature matching.
|
||||||
@ -121,10 +126,14 @@ abstract class MethodFingerprint(
|
|||||||
* @param context The [BytecodeContext] to host proxies.
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
* @return True if the resolution was successful, false otherwise.
|
* @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)
|
for (method in forClass.methods)
|
||||||
if (resolve(context, method, forClass))
|
if (resolve(context, method, forClass)) {
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,20 +145,26 @@ abstract class MethodFingerprint(
|
|||||||
* @param context The [BytecodeContext] to host proxies.
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
* @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise.
|
* @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
|
val methodFingerprint = this
|
||||||
|
|
||||||
if (methodFingerprint.result != null) return true
|
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
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags)
|
if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun parametersEqual(
|
fun parametersEqual(
|
||||||
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
parameters1: Iterable<CharSequence>,
|
||||||
|
parameters2: Iterable<CharSequence>,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (parameters1.count() != parameters2.count()) return false
|
if (parameters1.count() != parameters2.count()) return false
|
||||||
val iterator1 = parameters1.iterator()
|
val iterator1 = parameters1.iterator()
|
||||||
@ -159,15 +174,19 @@ abstract class MethodFingerprint(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methodFingerprint.parameters != null && !parametersEqual(
|
if (methodFingerprint.parameters != null &&
|
||||||
|
!parametersEqual(
|
||||||
methodFingerprint.parameters, // TODO: parseParameters()
|
methodFingerprint.parameters, // TODO: parseParameters()
|
||||||
method.parameterTypes
|
method.parameterTypes,
|
||||||
)
|
)
|
||||||
) return false
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||||
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass))
|
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass)) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val stringsScanResult: StringsScanResult? =
|
val stringsScanResult: StringsScanResult? =
|
||||||
if (methodFingerprint.strings != null) {
|
if (methodFingerprint.strings != null) {
|
||||||
@ -181,7 +200,9 @@ abstract class MethodFingerprint(
|
|||||||
if (
|
if (
|
||||||
instruction.opcode != Opcode.CONST_STRING &&
|
instruction.opcode != Opcode.CONST_STRING &&
|
||||||
instruction.opcode != Opcode.CONST_STRING_JUMBO
|
instruction.opcode != Opcode.CONST_STRING_JUMBO
|
||||||
) return@forEachIndexed
|
) {
|
||||||
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
|
||||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
val index = stringsList.indexOfFirst(string::contains)
|
val index = stringsList.indexOfFirst(string::contains)
|
||||||
@ -192,15 +213,19 @@ abstract class MethodFingerprint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stringsList.isNotEmpty()) return false
|
if (stringsList.isNotEmpty()) return false
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val patternScanResult = if (methodFingerprint.opcodes != null) {
|
val patternScanResult =
|
||||||
|
if (methodFingerprint.opcodes != null) {
|
||||||
method.implementation?.instructions ?: return false
|
method.implementation?.instructions ?: return false
|
||||||
|
|
||||||
fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.newWarnings(
|
fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.newWarnings(
|
||||||
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
pattern: Iterable<Opcode?>,
|
||||||
|
instructions: Iterable<Instruction>,
|
||||||
) = buildList {
|
) = buildList {
|
||||||
for ((patternIndex, instructionIndex) in (this@newWarnings.startIndex until this@newWarnings.endIndex).withIndex()) {
|
for ((patternIndex, instructionIndex) in (this@newWarnings.startIndex until this@newWarnings.endIndex).withIndex()) {
|
||||||
val originalOpcode = instructions.elementAt(instructionIndex).opcode
|
val originalOpcode = instructions.elementAt(instructionIndex).opcode
|
||||||
@ -213,14 +238,14 @@ abstract class MethodFingerprint(
|
|||||||
originalOpcode,
|
originalOpcode,
|
||||||
patternOpcode,
|
patternOpcode,
|
||||||
instructionIndex,
|
instructionIndex,
|
||||||
patternIndex
|
patternIndex,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Method.patternScan(
|
fun Method.patternScan(
|
||||||
fingerprint: MethodFingerprint
|
fingerprint: MethodFingerprint,
|
||||||
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
||||||
val instructions = this.implementation!!.instructions
|
val instructions = this.implementation!!.instructions
|
||||||
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
|
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
|
||||||
@ -253,7 +278,7 @@ abstract class MethodFingerprint(
|
|||||||
val result =
|
val result =
|
||||||
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
||||||
index,
|
index,
|
||||||
index + patternIndex
|
index + patternIndex,
|
||||||
)
|
)
|
||||||
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
|
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
|
||||||
result.warnings = result.newWarnings(pattern, instructions)
|
result.warnings = result.newWarnings(pattern, instructions)
|
||||||
@ -266,16 +291,19 @@ abstract class MethodFingerprint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
method.patternScan(methodFingerprint) ?: return false
|
method.patternScan(methodFingerprint) ?: return false
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
methodFingerprint.result = MethodFingerprintResult(
|
methodFingerprint.result =
|
||||||
|
MethodFingerprintResult(
|
||||||
method,
|
method,
|
||||||
forClass,
|
forClass,
|
||||||
MethodFingerprintResult.MethodFingerprintScanResult(
|
MethodFingerprintResult.MethodFingerprintScanResult(
|
||||||
patternScanResult,
|
patternScanResult,
|
||||||
stringsScanResult
|
stringsScanResult,
|
||||||
),
|
),
|
||||||
context
|
context,
|
||||||
)
|
)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -309,12 +337,13 @@ abstract class MethodFingerprint(
|
|||||||
* @param context The [BytecodeContext] to host proxies.
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
* @return True if the resolution was successful, false otherwise.
|
* @return True if the resolution was successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) =
|
fun Iterable<MethodFingerprint>.resolve(
|
||||||
forEach { fingerprint ->
|
context: BytecodeContext,
|
||||||
|
classes: Iterable<ClassDef>,
|
||||||
|
) = forEach { fingerprint ->
|
||||||
for (classDef in classes) {
|
for (classDef in classes) {
|
||||||
if (fingerprint.resolve(context, classDef)) break
|
if (fingerprint.resolve(context, classDef)) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class MethodFingerprintResult(
|
|||||||
val method: Method,
|
val method: Method,
|
||||||
val classDef: ClassDef,
|
val classDef: ClassDef,
|
||||||
val scanResult: MethodFingerprintScanResult,
|
val scanResult: MethodFingerprintScanResult,
|
||||||
internal val context: BytecodeContext
|
internal val context: BytecodeContext,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Returns a mutable clone of [classDef]
|
* Returns a mutable clone of [classDef]
|
||||||
@ -50,7 +50,7 @@ class MethodFingerprintResult(
|
|||||||
*/
|
*/
|
||||||
class MethodFingerprintScanResult(
|
class MethodFingerprintScanResult(
|
||||||
val patternScanResult: PatternScanResult?,
|
val patternScanResult: PatternScanResult?,
|
||||||
val stringsScanResult: StringsScanResult?
|
val stringsScanResult: StringsScanResult?,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* The result of scanning strings on the [MethodFingerprint].
|
* The result of scanning strings on the [MethodFingerprint].
|
||||||
@ -74,7 +74,7 @@ class MethodFingerprintResult(
|
|||||||
class PatternScanResult(
|
class PatternScanResult(
|
||||||
val startIndex: Int,
|
val startIndex: Int,
|
||||||
val endIndex: Int,
|
val endIndex: Int,
|
||||||
var warnings: List<Warning>? = null
|
var warnings: List<Warning>? = null,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Represents warnings of the pattern scan.
|
* Represents warnings of the pattern scan.
|
||||||
|
@ -8,5 +8,5 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
|||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
annotation class FuzzyPatternScanMethod(
|
annotation class FuzzyPatternScanMethod(
|
||||||
val threshold: Int = 1
|
val threshold: Int = 1,
|
||||||
)
|
)
|
@ -47,7 +47,6 @@ sealed class Patch<out T : Context<*>> {
|
|||||||
var use = true
|
var use = true
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
/**
|
/**
|
||||||
* Weather or not the patch requires integrations.
|
* Weather or not the patch requires integrations.
|
||||||
@ -64,7 +63,8 @@ sealed class Patch<out T : Context<*>> {
|
|||||||
this::class.findAnnotation<app.revanced.patcher.patch.annotation.Patch>()?.let { annotation ->
|
this::class.findAnnotation<app.revanced.patcher.patch.annotation.Patch>()?.let { annotation ->
|
||||||
name = annotation.name.ifEmpty { null }
|
name = annotation.name.ifEmpty { null }
|
||||||
description = annotation.description.ifEmpty { null }
|
description = annotation.description.ifEmpty { null }
|
||||||
compatiblePackages = annotation.compatiblePackages
|
compatiblePackages =
|
||||||
|
annotation.compatiblePackages
|
||||||
.map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) }
|
.map { CompatiblePackage(it.name, it.versions.toSet().ifEmpty { null }) }
|
||||||
.toSet().ifEmpty { null }
|
.toSet().ifEmpty { null }
|
||||||
dependencies = annotation.dependencies.toSet().ifEmpty { null }
|
dependencies = annotation.dependencies.toSet().ifEmpty { null }
|
||||||
|
@ -25,7 +25,7 @@ open class PatchOption<T>(
|
|||||||
val description: String?,
|
val description: String?,
|
||||||
val required: Boolean,
|
val required: Boolean,
|
||||||
val valueType: String,
|
val valueType: String,
|
||||||
val validator: PatchOption<T>.(T?) -> Boolean
|
val validator: PatchOption<T>.(T?) -> Boolean,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* The value of the [PatchOption].
|
* The value of the [PatchOption].
|
||||||
@ -45,6 +45,7 @@ open class PatchOption<T>(
|
|||||||
|
|
||||||
uncheckedValue = value
|
uncheckedValue = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of the [PatchOption].
|
* Get the value of the [PatchOption].
|
||||||
*
|
*
|
||||||
@ -81,9 +82,16 @@ open class PatchOption<T>(
|
|||||||
|
|
||||||
override fun toString() = value.toString()
|
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
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,9 +119,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<String>.(String?) -> Boolean = { true }
|
validator: PatchOption<String>.(String?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "String", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"String",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,9 +153,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Int?>.(Int?) -> Boolean = { true }
|
validator: PatchOption<Int?>.(Int?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "Int", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"Int",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,7 +187,7 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Boolean?>.(Boolean?) -> Boolean = { true }
|
validator: PatchOption<Boolean?>.(Boolean?) -> Boolean = { true },
|
||||||
) = PatchOption(key, default, values, title, description, required, "Boolean", validator).also {
|
) = PatchOption(key, default, values, title, description, required, "Boolean", validator).also {
|
||||||
registerOption(it)
|
registerOption(it)
|
||||||
}
|
}
|
||||||
@ -192,9 +214,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Float?>.(Float?) -> Boolean = { true }
|
validator: PatchOption<Float?>.(Float?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "Float", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"Float",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -219,9 +248,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Long?>.(Long?) -> Boolean = { true }
|
validator: PatchOption<Long?>.(Long?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "Long", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"Long",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,9 +282,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Array<String>?>.(Array<String>?) -> Boolean = { true }
|
validator: PatchOption<Array<String>?>.(Array<String>?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "StringArray", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"StringArray",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,9 +316,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Array<Int>?>.(Array<Int>?) -> Boolean = { true }
|
validator: PatchOption<Array<Int>?>.(Array<Int>?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "IntArray", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"IntArray",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -300,9 +350,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Array<Boolean>?>.(Array<Boolean>?) -> Boolean = { true }
|
validator: PatchOption<Array<Boolean>?>.(Array<Boolean>?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "BooleanArray", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"BooleanArray",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,9 +384,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Array<Float>?>.(Array<Float>?) -> Boolean = { true }
|
validator: PatchOption<Array<Float>?>.(Array<Float>?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "FloatArray", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"FloatArray",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,9 +418,16 @@ open class PatchOption<T>(
|
|||||||
title: String? = null,
|
title: String? = null,
|
||||||
description: String? = null,
|
description: String? = null,
|
||||||
required: Boolean = false,
|
required: Boolean = false,
|
||||||
validator: PatchOption<Array<Long>?>.(Array<Long>?) -> Boolean = { true }
|
validator: PatchOption<Array<Long>?>.(Array<Long>?) -> Boolean = { true },
|
||||||
) = PatchOption(
|
) = PatchOption(
|
||||||
key, default, values, title, description, required, "LongArray", validator
|
key,
|
||||||
|
default,
|
||||||
|
values,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
required,
|
||||||
|
"LongArray",
|
||||||
|
validator,
|
||||||
).also { registerOption(it) }
|
).also { registerOption(it) }
|
||||||
|
|
||||||
private fun <P : Patch<*>> P.registerOption(option: PatchOption<*>) = option.also { options.register(it) }
|
private fun <P : Patch<*>> P.registerOption(option: PatchOption<*>) = option.also { options.register(it) }
|
||||||
|
@ -36,6 +36,6 @@ sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage
|
|||||||
*
|
*
|
||||||
* @param key The key of the [PatchOption].
|
* @param key The key of the [PatchOption].
|
||||||
*/
|
*/
|
||||||
class PatchOptionNotFoundException(key: String)
|
class PatchOptionNotFoundException(key: String) :
|
||||||
: PatchOptionException("No option with key $key")
|
PatchOptionException("No option with key $key")
|
||||||
}
|
}
|
@ -1,13 +1,12 @@
|
|||||||
package app.revanced.patcher.patch.options
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of [PatchOption]s associated by their keys.
|
* A map of [PatchOption]s associated by their keys.
|
||||||
*
|
*
|
||||||
* @param options The [PatchOption]s to initialize with.
|
* @param options The [PatchOption]s to initialize with.
|
||||||
*/
|
*/
|
||||||
class PatchOptions internal constructor(
|
class PatchOptions internal constructor(
|
||||||
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf()
|
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf(),
|
||||||
) : MutableMap<String, PatchOption<*>> by options {
|
) : MutableMap<String, PatchOption<*>> by options {
|
||||||
/**
|
/**
|
||||||
* Register a [PatchOption]. Acts like [MutableMap.put].
|
* Register a [PatchOption]. Acts like [MutableMap.put].
|
||||||
@ -23,7 +22,10 @@ class PatchOptions internal constructor(
|
|||||||
* @param value The value.
|
* @param value The value.
|
||||||
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
||||||
*/
|
*/
|
||||||
operator fun <T : Any> set(key: String, value: T?) {
|
operator fun <T : Any> set(
|
||||||
|
key: String,
|
||||||
|
value: T?,
|
||||||
|
) {
|
||||||
val option = this[key]
|
val option = this[key]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -40,6 +42,5 @@ class PatchOptions internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Get an option.
|
* Get an option.
|
||||||
*/
|
*/
|
||||||
override operator fun get(key: String) =
|
override operator fun get(key: String) = options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
||||||
options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
|
||||||
}
|
}
|
@ -34,7 +34,10 @@ internal object ClassMerger {
|
|||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
* @return The merged class or the original class if no merge was needed.
|
* @return The merged class or the original class if no merge was needed.
|
||||||
*/
|
*/
|
||||||
fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext) = this
|
fun ClassDef.merge(
|
||||||
|
otherClass: ClassDef,
|
||||||
|
context: BytecodeContext,
|
||||||
|
) = this
|
||||||
// .fixFieldAccess(otherClass)
|
// .fixFieldAccess(otherClass)
|
||||||
// .fixMethodAccess(otherClass)
|
// .fixMethodAccess(otherClass)
|
||||||
.addMissingFields(otherClass)
|
.addMissingFields(otherClass)
|
||||||
@ -47,7 +50,8 @@ internal object ClassMerger {
|
|||||||
* @param fromClass The class to add missing methods from.
|
* @param fromClass The class to add missing methods from.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef {
|
private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef {
|
||||||
val missingMethods = fromClass.methods.let { fromMethods ->
|
val missingMethods =
|
||||||
|
fromClass.methods.let { fromMethods ->
|
||||||
methods.filterNot { method ->
|
methods.filterNot { method ->
|
||||||
fromMethods.any { fromMethod ->
|
fromMethods.any { fromMethod ->
|
||||||
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
||||||
@ -70,7 +74,8 @@ internal object ClassMerger {
|
|||||||
* @param fromClass The class to add missing fields from.
|
* @param fromClass The class to add missing fields from.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef {
|
private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef {
|
||||||
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
val missingFields =
|
||||||
|
fields.filterNotAny(fromClass.fields) { field, fromField ->
|
||||||
fromField.name == field.name
|
fromField.name == field.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +93,10 @@ internal object ClassMerger {
|
|||||||
* @param reference The class to check the [AccessFlags] of.
|
* @param reference The class to check the [AccessFlags] of.
|
||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext) =
|
private fun ClassDef.publicize(
|
||||||
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
reference: ClassDef,
|
||||||
|
context: BytecodeContext,
|
||||||
|
) = if (reference.accessFlags.isPublic() && !accessFlags.isPublic()) {
|
||||||
this.asMutableClass().apply {
|
this.asMutableClass().apply {
|
||||||
context.traverseClassHierarchy(this) {
|
context.traverseClassHierarchy(this) {
|
||||||
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||||
@ -99,7 +106,9 @@ internal object ClassMerger {
|
|||||||
accessFlags = accessFlags.toPublic()
|
accessFlags = accessFlags.toPublic()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else this
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publicize fields if they are public in [reference].
|
* Publicize fields if they are public in [reference].
|
||||||
@ -107,7 +116,8 @@ internal object ClassMerger {
|
|||||||
* @param reference The class to check the [AccessFlags] of the fields in.
|
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef {
|
private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef {
|
||||||
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
val brokenFields =
|
||||||
|
fields.filterAny(reference.fields) { field, referenceField ->
|
||||||
if (field.name != referenceField.name) return@filterAny false
|
if (field.name != referenceField.name) return@filterAny false
|
||||||
|
|
||||||
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
||||||
@ -135,7 +145,8 @@ internal object ClassMerger {
|
|||||||
* @param reference The class to check the [AccessFlags] of the methods in.
|
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef {
|
private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef {
|
||||||
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
val brokenMethods =
|
||||||
|
methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||||
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||||
|
|
||||||
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
||||||
@ -164,7 +175,10 @@ internal object ClassMerger {
|
|||||||
* @param targetClass the class to start traversing the class hierarchy from
|
* @param targetClass the class to start traversing the class hierarchy from
|
||||||
* @param callback function that is called for every class in the hierarchy
|
* @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)
|
callback(targetClass)
|
||||||
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||||
traverseClassHierarchy(it, callback)
|
traverseClassHierarchy(it, callback)
|
||||||
@ -195,7 +209,8 @@ internal object ClassMerger {
|
|||||||
* @return The [this] filtered on [needles] matching the given [predicate].
|
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||||
*/
|
*/
|
||||||
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
||||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
needles: Iterable<NeedleType>,
|
||||||
|
predicate: (HayType, NeedleType) -> Boolean,
|
||||||
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,13 +221,14 @@ internal object ClassMerger {
|
|||||||
* @return The [this] filtered on [needles] not matching the given [predicate].
|
* @return The [this] filtered on [needles] not matching the given [predicate].
|
||||||
*/
|
*/
|
||||||
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
||||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
needles: Iterable<NeedleType>,
|
||||||
|
predicate: (HayType, NeedleType) -> Boolean,
|
||||||
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
||||||
|
|
||||||
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
||||||
haystack: Iterable<HayType>,
|
haystack: Iterable<HayType>,
|
||||||
needles: Iterable<NeedleType>,
|
needles: Iterable<NeedleType>,
|
||||||
predicate: (HayType, NeedleType) -> Boolean
|
predicate: (HayType, NeedleType) -> Boolean,
|
||||||
) = this(haystack) { hay ->
|
) = this(haystack) { hay ->
|
||||||
needles.any { needle ->
|
needles.any { needle ->
|
||||||
predicate(hay, needle)
|
predicate(hay, needle)
|
||||||
|
@ -31,10 +31,10 @@ class DomFileEditor internal constructor(
|
|||||||
/**
|
/**
|
||||||
* The document of the xml file
|
* The document of the xml file
|
||||||
*/
|
*/
|
||||||
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
val file: Document =
|
||||||
|
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
||||||
.also(Document::normalize)
|
.also(Document::normalize)
|
||||||
|
|
||||||
|
|
||||||
// lazily open an output stream
|
// 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
|
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
||||||
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
||||||
@ -58,7 +58,8 @@ class DomFileEditor internal constructor(
|
|||||||
outputStream?.let {
|
outputStream?.let {
|
||||||
// prevent writing to same file, if it is being locked
|
// prevent writing to same file, if it is being locked
|
||||||
// isLocked will be false if the editor was created through a stream
|
// isLocked will be false if the editor was created through a stream
|
||||||
val isLocked = filePath?.let { path ->
|
val isLocked =
|
||||||
|
filePath?.let { path ->
|
||||||
val isLocked = locks[path]!! > 1
|
val isLocked = locks[path]!! > 1
|
||||||
// decrease the lock count if the editor was opened for a file
|
// decrease the lock count if the editor was opened for a file
|
||||||
locks.merge(path, -1, Integer::sum)
|
locks.merge(path, -1, Integer::sum)
|
||||||
|
@ -19,7 +19,8 @@ class ProxyClassList internal constructor(classes: MutableSet<ClassDef>) : Mutab
|
|||||||
/**
|
/**
|
||||||
* Replace all classes with their mutated versions.
|
* Replace all classes with their mutated versions.
|
||||||
*/
|
*/
|
||||||
internal fun replaceClasses() = proxies.removeIf { proxy ->
|
internal fun replaceClasses() =
|
||||||
|
proxies.removeIf { proxy ->
|
||||||
// If the proxy is unused, return false to keep it in the proxies list.
|
// If the proxy is unused, return false to keep it in the proxies list.
|
||||||
if (!proxy.resolved) return@removeIf false
|
if (!proxy.resolved) return@removeIf false
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
|||||||
*/
|
*/
|
||||||
class MethodWalker internal constructor(
|
class MethodWalker internal constructor(
|
||||||
private val bytecodeContext: BytecodeContext,
|
private val bytecodeContext: BytecodeContext,
|
||||||
private var currentMethod: Method
|
private var currentMethod: Method,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Get the method which was walked last.
|
* 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.
|
* @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].
|
* @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 ->
|
currentMethod.implementation?.instructions?.let { instructions ->
|
||||||
val instruction = instructions.elementAt(offset)
|
val instruction = instructions.elementAt(offset)
|
||||||
|
|
||||||
@ -44,7 +47,8 @@ class MethodWalker internal constructor(
|
|||||||
val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
|
val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
|
||||||
|
|
||||||
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
||||||
currentMethod = methods.first {
|
currentMethod =
|
||||||
|
methods.first {
|
||||||
return@first MethodUtil.methodSignaturesMatch(it, newMethod)
|
return@first MethodUtil.methodSignaturesMatch(it, newMethod)
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
|
@ -27,7 +27,8 @@ class ClassProxy internal constructor(
|
|||||||
resolved = true
|
resolved = true
|
||||||
if (immutableClass is MutableClass) {
|
if (immutableClass is MutableClass) {
|
||||||
immutableClass
|
immutableClass
|
||||||
} else
|
} else {
|
||||||
MutableClass(immutableClass)
|
MutableClass(immutableClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -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.MutableAnnotation.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.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.base.reference.BaseTypeReference
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.util.FieldUtil
|
import com.android.tools.smali.dexlib2.util.FieldUtil
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
import com.google.common.collect.Iterables
|
||||||
|
|
||||||
class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() {
|
class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() {
|
||||||
// Class
|
// Class
|
||||||
|
@ -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.AnnotationElement
|
||||||
import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue
|
||||||
|
|
||||||
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : BaseAnnotationEncodedValue(),
|
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) :
|
||||||
|
BaseAnnotationEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var type = annotationEncodedValue.type
|
private var type = annotationEncodedValue.type
|
||||||
|
|
||||||
|
@ -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.base.value.BaseBooleanEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue
|
||||||
|
|
||||||
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : BaseBooleanEncodedValue(),
|
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) :
|
||||||
|
BaseBooleanEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = booleanEncodedValue.value
|
private var value = booleanEncodedValue.value
|
||||||
|
|
||||||
|
@ -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.base.value.BaseDoubleEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue
|
||||||
|
|
||||||
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDoubleEncodedValue(),
|
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) :
|
||||||
|
BaseDoubleEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = doubleEncodedValue.value
|
private var value = doubleEncodedValue.value
|
||||||
|
|
||||||
|
@ -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.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.value.MethodEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.MethodEncodedValue
|
||||||
|
|
||||||
class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) : BaseMethodEncodedValue(),
|
class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) :
|
||||||
|
BaseMethodEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = methodEncodedValue.value
|
private var value = methodEncodedValue.value
|
||||||
|
|
||||||
|
@ -22,6 +22,4 @@ class MutableMethodHandleEncodedValue(methodHandleEncodedValue: MethodHandleEnco
|
|||||||
return MutableMethodHandleEncodedValue(this)
|
return MutableMethodHandleEncodedValue(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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.reference.MethodProtoReference
|
||||||
import com.android.tools.smali.dexlib2.iface.value.MethodTypeEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.MethodTypeEncodedValue
|
||||||
|
|
||||||
class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedValue) : BaseMethodTypeEncodedValue(),
|
class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedValue) :
|
||||||
|
BaseMethodTypeEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = methodTypeEncodedValue.value
|
private var value = methodTypeEncodedValue.value
|
||||||
|
|
||||||
@ -21,6 +22,4 @@ class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedVal
|
|||||||
return MutableMethodTypeEncodedValue(this)
|
return MutableMethodTypeEncodedValue(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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.ByteEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
|
||||||
|
|
||||||
class MutableStringEncodedValue(stringEncodedValue: StringEncodedValue) : BaseStringEncodedValue(),
|
class MutableStringEncodedValue(stringEncodedValue: StringEncodedValue) :
|
||||||
|
BaseStringEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = stringEncodedValue.value
|
private var value = stringEncodedValue.value
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package app.revanced.patcher.util.smali
|
package app.revanced.patcher.util.smali
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
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.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcodes
|
import com.android.tools.smali.dexlib2.Opcodes
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
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.smaliFlexLexer
|
||||||
import com.android.tools.smali.smali.smaliParser
|
import com.android.tools.smali.smali.smaliParser
|
||||||
import com.android.tools.smali.smali.smaliTreeWalker
|
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.io.InputStreamReader
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -32,14 +32,22 @@ class InlineSmaliCompiler {
|
|||||||
* if the parameters and registers of the method are passed.
|
* if the parameters and registers of the method are passed.
|
||||||
*/
|
*/
|
||||||
fun compile(
|
fun compile(
|
||||||
instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean
|
instructions: String,
|
||||||
|
parameters: String,
|
||||||
|
registers: Int,
|
||||||
|
forStaticMethod: Boolean,
|
||||||
): List<BuilderInstruction> {
|
): List<BuilderInstruction> {
|
||||||
val input = METHOD_TEMPLATE.format(Locale.ENGLISH,
|
val input =
|
||||||
|
METHOD_TEMPLATE.format(
|
||||||
|
Locale.ENGLISH,
|
||||||
if (forStaticMethod) {
|
if (forStaticMethod) {
|
||||||
"static"
|
"static"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
}, parameters, registers, instructions
|
},
|
||||||
|
parameters,
|
||||||
|
registers,
|
||||||
|
instructions,
|
||||||
)
|
)
|
||||||
val reader = InputStreamReader(input.byteInputStream())
|
val reader = InputStreamReader(input.byteInputStream())
|
||||||
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
|
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
|
||||||
@ -48,7 +56,7 @@ class InlineSmaliCompiler {
|
|||||||
val result = parser.smali_file()
|
val result = parser.smali_file()
|
||||||
if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) {
|
if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) {
|
||||||
throw IllegalStateException(
|
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)
|
val treeStream = CommonTreeNodeStream(result.tree)
|
||||||
@ -70,10 +78,11 @@ class InlineSmaliCompiler {
|
|||||||
* @returns A list of instructions.
|
* @returns A list of instructions.
|
||||||
*/
|
*/
|
||||||
fun String.toInstructions(method: MutableMethod? = null): List<BuilderInstruction> {
|
fun String.toInstructions(method: MutableMethod? = null): List<BuilderInstruction> {
|
||||||
return InlineSmaliCompiler.compile(this,
|
return InlineSmaliCompiler.compile(
|
||||||
|
this,
|
||||||
method?.parameters?.joinToString("") { it } ?: "",
|
method?.parameters?.joinToString("") { it } ?: "",
|
||||||
method?.implementation?.registerCount ?: 1,
|
method?.implementation?.registerCount ?: 1,
|
||||||
method?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true
|
method?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ private object InstructionExtensionsTest {
|
|||||||
private lateinit var testMethodImplementation: MutableMethodImplementation
|
private lateinit var testMethodImplementation: MutableMethodImplementation
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun createTestMethod() = ImmutableMethod(
|
fun createTestMethod() =
|
||||||
|
ImmutableMethod(
|
||||||
"TestClass;",
|
"TestClass;",
|
||||||
"testMethod",
|
"testMethod",
|
||||||
null,
|
null,
|
||||||
@ -41,7 +42,8 @@ private object InstructionExtensionsTest {
|
|||||||
).let { testMethod = it.toMutable() }
|
).let { testMethod = it.toMutable() }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionsToImplementationIndexed() = applyToImplementation {
|
fun addInstructionsToImplementationIndexed() =
|
||||||
|
applyToImplementation {
|
||||||
addInstructions(5, getTestInstructions(5..6)).also {
|
addInstructions(5, getTestInstructions(5..6)).also {
|
||||||
assertRegisterIs(5, 5)
|
assertRegisterIs(5, 5)
|
||||||
assertRegisterIs(6, 6)
|
assertRegisterIs(6, 6)
|
||||||
@ -51,7 +53,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionsToImplementation() = applyToImplementation {
|
fun addInstructionsToImplementation() =
|
||||||
|
applyToImplementation {
|
||||||
addInstructions(getTestInstructions(10..11)).also {
|
addInstructions(getTestInstructions(10..11)).also {
|
||||||
assertRegisterIs(10, 10)
|
assertRegisterIs(10, 10)
|
||||||
assertRegisterIs(11, 11)
|
assertRegisterIs(11, 11)
|
||||||
@ -59,19 +62,22 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeInstructionsFromImplementationIndexed() = applyToImplementation {
|
fun removeInstructionsFromImplementationIndexed() =
|
||||||
|
applyToImplementation {
|
||||||
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeInstructionsFromImplementation() = applyToImplementation {
|
fun removeInstructionsFromImplementation() =
|
||||||
|
applyToImplementation {
|
||||||
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun replaceInstructionsInImplementationIndexed() = applyToImplementation {
|
fun replaceInstructionsInImplementationIndexed() =
|
||||||
|
applyToImplementation {
|
||||||
replaceInstructions(5, getTestInstructions(0..1)).also {
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
@ -80,27 +86,32 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionToMethodIndexed() = applyToMethod {
|
fun addInstructionToMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionToMethod() = applyToMethod {
|
fun addInstructionToMethod() =
|
||||||
|
applyToMethod {
|
||||||
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
|
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSmaliInstructionToMethodIndexed() = applyToMethod {
|
fun addSmaliInstructionToMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
|
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSmaliInstructionToMethod() = applyToMethod {
|
fun addSmaliInstructionToMethod() =
|
||||||
|
applyToMethod {
|
||||||
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
|
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionsToMethodIndexed() = applyToMethod {
|
fun addInstructionsToMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
addInstructions(5, getTestInstructions(0..1)).also {
|
addInstructions(5, getTestInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
@ -110,7 +121,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addInstructionsToMethod() = applyToMethod {
|
fun addInstructionsToMethod() =
|
||||||
|
applyToMethod {
|
||||||
addInstructions(getTestInstructions(0..1)).also {
|
addInstructions(getTestInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 10)
|
assertRegisterIs(0, 10)
|
||||||
assertRegisterIs(1, 11)
|
assertRegisterIs(1, 11)
|
||||||
@ -120,7 +132,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSmaliInstructionsToMethodIndexed() = applyToMethod {
|
fun addSmaliInstructionsToMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
|
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
@ -130,7 +143,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSmaliInstructionsToMethod() = applyToMethod {
|
fun addSmaliInstructionsToMethod() =
|
||||||
|
applyToMethod {
|
||||||
addInstructions(getTestSmaliInstructions(0..1)).also {
|
addInstructions(getTestSmaliInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 10)
|
assertRegisterIs(0, 10)
|
||||||
assertRegisterIs(1, 11)
|
assertRegisterIs(1, 11)
|
||||||
@ -140,19 +154,21 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod {
|
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
val label = ExternalLabel("testLabel", getInstruction(5))
|
val label = ExternalLabel("testLabel", getInstruction(5))
|
||||||
|
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
5,
|
5,
|
||||||
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
|
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
|
||||||
label
|
label,
|
||||||
).also {
|
).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
assertRegisterIs(5, 8)
|
assertRegisterIs(5, 8)
|
||||||
|
|
||||||
val gotoTarget = getInstruction<BuilderOffsetInstruction>(7)
|
val gotoTarget =
|
||||||
|
getInstruction<BuilderOffsetInstruction>(7)
|
||||||
.target.location.instruction as OneRegisterInstruction
|
.target.location.instruction as OneRegisterInstruction
|
||||||
|
|
||||||
assertEquals(5, gotoTarget.registerA)
|
assertEquals(5, gotoTarget.registerA)
|
||||||
@ -160,7 +176,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeInstructionFromMethodIndexed() = applyToMethod {
|
fun removeInstructionFromMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
removeInstruction(5).also {
|
removeInstruction(5).also {
|
||||||
assertRegisterIs(4, 4)
|
assertRegisterIs(4, 4)
|
||||||
assertRegisterIs(6, 5)
|
assertRegisterIs(6, 5)
|
||||||
@ -168,24 +185,28 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeInstructionsFromMethodIndexed() = applyToMethod {
|
fun removeInstructionsFromMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeInstructionsFromMethod() = applyToMethod {
|
fun removeInstructionsFromMethod() =
|
||||||
|
applyToMethod {
|
||||||
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun replaceInstructionInMethodIndexed() = applyToMethod {
|
fun replaceInstructionInMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun replaceInstructionsInMethodIndexed() = applyToMethod {
|
fun replaceInstructionsInMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
replaceInstructions(5, getTestInstructions(0..1)).also {
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
@ -194,7 +215,8 @@ private object InstructionExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod {
|
fun replaceSmaliInstructionsInMethodIndexed() =
|
||||||
|
applyToMethod {
|
||||||
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
|
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
|
||||||
assertRegisterIs(0, 5)
|
assertRegisterIs(0, 5)
|
||||||
assertRegisterIs(1, 6)
|
assertRegisterIs(1, 6)
|
||||||
@ -212,18 +234,25 @@ private object InstructionExtensionsTest {
|
|||||||
testMethod.apply(block)
|
testMethod.apply(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals(
|
private fun MutableMethodImplementation.assertRegisterIs(
|
||||||
register, getInstruction<OneRegisterInstruction>(atIndex).registerA
|
register: Int,
|
||||||
|
atIndex: Int,
|
||||||
|
) = assertEquals(
|
||||||
|
register,
|
||||||
|
getInstruction<OneRegisterInstruction>(atIndex).registerA,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) =
|
private fun MutableMethod.assertRegisterIs(
|
||||||
implementation!!.assertRegisterIs(register, atIndex)
|
register: Int,
|
||||||
|
atIndex: Int,
|
||||||
|
) = implementation!!.assertRegisterIs(register, atIndex)
|
||||||
|
|
||||||
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
|
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
|
||||||
|
|
||||||
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
|
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
|
||||||
|
|
||||||
private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") {
|
private fun getTestSmaliInstructions(range: IntRange) =
|
||||||
|
range.joinToString("\n") {
|
||||||
getTestSmaliInstruction(it)
|
getTestSmaliInstruction(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,7 @@ internal class PatchOptionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should allow setting custom value`() =
|
fun `should allow setting custom value`() = assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = "unknown" }
|
||||||
assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = "unknown" }
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should allow resetting value`() = assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = null }
|
fun `should allow resetting value`() = assertDoesNotThrow { OptionsTestPatch.stringOptionWithChoices = null }
|
||||||
@ -86,40 +85,40 @@ internal class PatchOptionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `option types should be known`() =
|
fun `option types should be known`() = assertTrue(OptionsTestPatch.options["array"].valueType == "StringArray")
|
||||||
assertTrue(OptionsTestPatch.options["array"].valueType == "StringArray")
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getting default value should work`() =
|
fun `getting default value should work`() = assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) }
|
||||||
assertDoesNotThrow { assertNull(OptionsTestPatch.resettableOption.default) }
|
|
||||||
|
|
||||||
private object OptionsTestPatch : BytecodePatch() {
|
private object OptionsTestPatch : BytecodePatch() {
|
||||||
var booleanOption by booleanPatchOption(
|
var booleanOption by booleanPatchOption(
|
||||||
"bool",
|
"bool",
|
||||||
true
|
true,
|
||||||
)
|
)
|
||||||
var requiredStringOption by stringPatchOption(
|
var requiredStringOption by stringPatchOption(
|
||||||
"required",
|
"required",
|
||||||
"default",
|
"default",
|
||||||
required = true
|
required = true,
|
||||||
)
|
)
|
||||||
var stringArrayOption = stringArrayPatchOption(
|
var stringArrayOption =
|
||||||
|
stringArrayPatchOption(
|
||||||
"array",
|
"array",
|
||||||
arrayOf("1", "2")
|
arrayOf("1", "2"),
|
||||||
)
|
)
|
||||||
var stringOptionWithChoices by stringPatchOption(
|
var stringOptionWithChoices by stringPatchOption(
|
||||||
"choices",
|
"choices",
|
||||||
"value",
|
"value",
|
||||||
values = mapOf("Valid option value" to "valid")
|
values = mapOf("Valid option value" to "valid"),
|
||||||
)
|
)
|
||||||
var validatedOption by stringPatchOption(
|
var validatedOption by stringPatchOption(
|
||||||
|
|
||||||
"validated",
|
"validated",
|
||||||
"default"
|
"default",
|
||||||
) { it == "valid" }
|
) { it == "valid" }
|
||||||
var resettableOption = stringPatchOption(
|
var resettableOption =
|
||||||
"resettable", null,
|
stringPatchOption(
|
||||||
required = true
|
"resettable",
|
||||||
|
null,
|
||||||
|
required = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableList
|
|||||||
name = "Example bytecode patch",
|
name = "Example bytecode patch",
|
||||||
description = "Example demonstration of a bytecode patch.",
|
description = "Example demonstration of a bytecode patch.",
|
||||||
dependencies = [ExampleResourcePatch::class],
|
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)) {
|
object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
||||||
// Entry point of a patch. Supplied fingerprints are resolved at this point.
|
// 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;
|
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||||
move-result-object v1
|
move-result-object v1
|
||||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,14 +82,14 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
|||||||
BuilderInstruction21c(
|
BuilderInstruction21c(
|
||||||
Opcode.CONST_STRING,
|
Opcode.CONST_STRING,
|
||||||
0,
|
0,
|
||||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
ImmutableStringReference("Hello, ReVanced! Adding bytecode."),
|
||||||
),
|
),
|
||||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0),
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
)
|
),
|
||||||
).toMutable()
|
).toMutable(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add a field in the main class.
|
// Add a field in the main class.
|
||||||
@ -105,12 +105,12 @@ object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
|||||||
ImmutableFieldReference(
|
ImmutableFieldReference(
|
||||||
"Ljava/lang/System;",
|
"Ljava/lang/System;",
|
||||||
"out",
|
"out",
|
||||||
"Ljava/io/PrintStream;"
|
"Ljava/io/PrintStream;",
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
).toMutable()
|
).toMutable(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw PatchException("Fingerprint failed to resolve.")
|
} ?: 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 index The index of the instruction to replace.
|
||||||
* @param string The replacement string.
|
* @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)
|
val instruction = getInstruction(index)
|
||||||
|
|
||||||
// Utility method of dexlib2.
|
// 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.
|
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
||||||
replaceInstruction(
|
replaceInstruction(
|
||||||
index,
|
index,
|
||||||
"const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}"
|
"const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.patch.usage
|
package app.revanced.patcher.patch.usage
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
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.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ object ExampleFingerprint : MethodFingerprint(
|
|||||||
Opcode.SGET_OBJECT,
|
Opcode.SGET_OBJECT,
|
||||||
null, // Matching unknown opcodes.
|
null, // Matching unknown opcodes.
|
||||||
Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching.
|
Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching.
|
||||||
Opcode.RETURN_VOID
|
Opcode.RETURN_VOID,
|
||||||
),
|
),
|
||||||
null
|
null,
|
||||||
)
|
)
|
@ -4,18 +4,18 @@ import app.revanced.patcher.data.ResourceContext
|
|||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
|
||||||
class ExampleResourcePatch : ResourcePatch() {
|
class ExampleResourcePatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
||||||
val element = editor // regular DomFileEditor
|
val element =
|
||||||
|
editor // regular DomFileEditor
|
||||||
.file
|
.file
|
||||||
.getElementsByTagName("application")
|
.getElementsByTagName("application")
|
||||||
.item(0) as Element
|
.item(0) as Element
|
||||||
element
|
element
|
||||||
.setAttribute(
|
.setAttribute(
|
||||||
"exampleAttribute",
|
"exampleAttribute",
|
||||||
"exampleValue"
|
"exampleValue",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,18 @@ internal class InlineSmaliCompilerTest {
|
|||||||
val insnIndex = insnAmount - 2
|
val insnIndex = insnAmount - 2
|
||||||
val targetIndex = insnIndex - 1
|
val targetIndex = insnIndex - 1
|
||||||
|
|
||||||
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
method.addInstructions(
|
||||||
|
arrayOfNulls<String>(insnAmount).also {
|
||||||
Arrays.fill(it, "const/4 v0, 0x0")
|
Arrays.fill(it, "const/4 v0, 0x0")
|
||||||
}.joinToString("\n"))
|
}.joinToString("\n"),
|
||||||
|
)
|
||||||
method.addInstructionsWithLabels(
|
method.addInstructionsWithLabels(
|
||||||
targetIndex,
|
targetIndex,
|
||||||
"""
|
"""
|
||||||
:test
|
:test
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
if-eqz v0, :test
|
if-eqz v0, :test
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
@ -59,7 +61,7 @@ internal class InlineSmaliCompilerTest {
|
|||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
|
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
|
||||||
@ -71,7 +73,7 @@ internal class InlineSmaliCompilerTest {
|
|||||||
if-eqz v0, :test
|
if-eqz v0, :test
|
||||||
return-void
|
return-void
|
||||||
""",
|
""",
|
||||||
ExternalLabel("test", method.getInstruction(1))
|
ExternalLabel("test", method.getInstruction(1)),
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
@ -93,10 +95,13 @@ internal class InlineSmaliCompilerTest {
|
|||||||
accessFlags,
|
accessFlags,
|
||||||
emptySet(),
|
emptySet(),
|
||||||
emptySet(),
|
emptySet(),
|
||||||
MutableMethodImplementation(registerCount)
|
MutableMethodImplementation(registerCount),
|
||||||
).toMutable()
|
).toMutable()
|
||||||
|
|
||||||
private fun instructionEquals(want: BuilderInstruction, have: BuilderInstruction) {
|
private fun instructionEquals(
|
||||||
|
want: BuilderInstruction,
|
||||||
|
have: BuilderInstruction,
|
||||||
|
) {
|
||||||
assertEquals(want.opcode, have.opcode)
|
assertEquals(want.opcode, have.opcode)
|
||||||
assertEquals(want.format, have.format)
|
assertEquals(want.format, have.format)
|
||||||
assertEquals(want.codeUnits, have.codeUnits)
|
assertEquals(want.codeUnits, have.codeUnits)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user