mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-27 04:31:03 +00:00
93 lines
3.6 KiB
Kotlin
93 lines
3.6 KiB
Kotlin
package app.revanced.util.patch
|
|
|
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
import com.android.tools.smali.dexlib2.Opcode
|
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
|
|
typealias Instruction35cInfo = Triple<IMethodCall, Instruction35c, Int>
|
|
|
|
interface IMethodCall {
|
|
val definedClassName: String
|
|
val methodName: String
|
|
val methodParams: Array<String>
|
|
val returnType: String
|
|
|
|
/**
|
|
* Replaces an invoke-virtual instruction with an invoke-static instruction,
|
|
* which calls a static replacement method in the respective integrations class.
|
|
* The method definition in the integrations class is expected to be the same,
|
|
* except that the method should be static and take as a first parameter
|
|
* an instance of the class, in which the original method was defined in.
|
|
*
|
|
* Example:
|
|
*
|
|
* original method: Window#setFlags(int, int)
|
|
*
|
|
* replacement method: Integrations#setFlags(Window, int, int)
|
|
*/
|
|
fun replaceInvokeVirtualWithIntegrations(
|
|
definingClassDescriptor: String,
|
|
method: MutableMethod,
|
|
instruction: Instruction35c,
|
|
instructionIndex: Int
|
|
) {
|
|
val registers = arrayOf(
|
|
instruction.registerC,
|
|
instruction.registerD,
|
|
instruction.registerE,
|
|
instruction.registerF,
|
|
instruction.registerG
|
|
)
|
|
val argsNum = methodParams.size + 1 // + 1 for instance of definedClassName
|
|
if (argsNum > registers.size) {
|
|
// should never happen, but just to be sure (also for the future) a safety check
|
|
throw RuntimeException(
|
|
"Not enough registers for ${definedClassName}#${methodName}: " +
|
|
"Required $argsNum registers, but only got ${registers.size}."
|
|
)
|
|
}
|
|
|
|
val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v${reg}" }
|
|
val replacementMethodDefinition =
|
|
"${methodName}(${definedClassName}${methodParams.joinToString(separator = "")})${returnType}"
|
|
|
|
method.replaceInstruction(
|
|
instructionIndex,
|
|
"invoke-static { $args }, ${definingClassDescriptor}->${replacementMethodDefinition}"
|
|
)
|
|
}
|
|
}
|
|
|
|
inline fun <reified E> fromMethodReference(methodReference: MethodReference)
|
|
where E : Enum<E>, E : IMethodCall = enumValues<E>().firstOrNull { search ->
|
|
search.definedClassName == methodReference.definingClass
|
|
&& search.methodName == methodReference.name
|
|
&& methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
|
|
}
|
|
|
|
inline fun <reified E> filterMapInstruction35c(
|
|
integrationsClassDescriptorPrefix: String,
|
|
classDef: ClassDef,
|
|
instruction: Instruction,
|
|
instructionIndex: Int
|
|
): Instruction35cInfo? where E : Enum<E>, E : IMethodCall {
|
|
if (classDef.type.startsWith(integrationsClassDescriptorPrefix)) {
|
|
// avoid infinite recursion
|
|
return null
|
|
}
|
|
|
|
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) {
|
|
return null
|
|
}
|
|
|
|
val invokeInstruction = instruction as Instruction35c
|
|
val methodRef = invokeInstruction.reference as MethodReference
|
|
val methodCall = fromMethodReference<E>(methodRef) ?: return null
|
|
|
|
return Instruction35cInfo(methodCall, invokeInstruction, instructionIndex)
|
|
}
|