mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-11 13:56:16 +00:00
450 lines
16 KiB
Kotlin
450 lines
16 KiB
Kotlin
package app.revanced.patcher.extensions
|
|
|
|
import app.revanced.patcher.dex.mutable.MutableMethod
|
|
import app.revanced.patcher.util.toInstructions
|
|
import com.android.tools.smali.dexlib2.AccessFlags
|
|
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
|
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
|
import com.android.tools.smali.dexlib2.builder.Label
|
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
|
import com.android.tools.smali.dexlib2.builder.instruction.*
|
|
import com.android.tools.smali.dexlib2.iface.Method
|
|
import com.android.tools.smali.dexlib2.iface.MethodImplementation
|
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|
|
|
fun Method.accessFlags(vararg flags: AccessFlags) =
|
|
accessFlags.and(flags.map { it.ordinal }.reduce { acc, i -> acc or i }) != 0
|
|
|
|
/**
|
|
* Add instructions to a method at the given index.
|
|
*
|
|
* @param index The index to add the instructions at.
|
|
* @param instructions The instructions to add.
|
|
*/
|
|
fun MutableMethodImplementation.addInstructions(
|
|
index: Int,
|
|
instructions: List<BuilderInstruction>,
|
|
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
|
|
|
/**
|
|
* Add instructions to a method.
|
|
* The instructions will be added at the end of the method.
|
|
*
|
|
* @param instructions The instructions to add.
|
|
*/
|
|
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
|
|
instructions.forEach { addInstruction(it) }
|
|
|
|
/**
|
|
* Remove instructions from a method at the given index.
|
|
*
|
|
* @param index The index to remove the instructions at.
|
|
* @param count The amount of instructions to remove.
|
|
*/
|
|
fun MutableMethodImplementation.removeInstructions(
|
|
index: Int,
|
|
count: Int,
|
|
) = repeat(count) {
|
|
removeInstruction(index)
|
|
}
|
|
|
|
/**
|
|
* Remove the first instructions from a method.
|
|
*
|
|
* @param count The amount of instructions to remove.
|
|
*/
|
|
fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count)
|
|
|
|
/**
|
|
* Replace instructions at the given index with the given instructions.
|
|
* The amount of instructions to replace is the amount of instructions in the given list.
|
|
*
|
|
* @param index The index to replace the instructions at.
|
|
* @param instructions The instructions to replace the instructions with.
|
|
*/
|
|
fun MutableMethodImplementation.replaceInstructions(
|
|
index: Int,
|
|
instructions: List<BuilderInstruction>,
|
|
) {
|
|
// Remove the instructions at the given index.
|
|
removeInstructions(index, instructions.size)
|
|
|
|
// Add the instructions at the given index.
|
|
addInstructions(index, instructions)
|
|
}
|
|
|
|
/**
|
|
* Add an instruction to a method at the given index.
|
|
*
|
|
* @param index The index to add the instruction at.
|
|
* @param instruction The instruction to add.
|
|
*/
|
|
fun MutableMethod.addInstruction(
|
|
index: Int,
|
|
instruction: BuilderInstruction,
|
|
) = implementation!!.addInstruction(index, instruction)
|
|
|
|
/**
|
|
* Add an instruction to a method.
|
|
*
|
|
* @param instruction The instructions to add.
|
|
*/
|
|
fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction)
|
|
|
|
/**
|
|
* Add an instruction to a method at the given index.
|
|
*
|
|
* @param index The index to add the instruction at.
|
|
* @param smaliInstructions The instruction to add.
|
|
*/
|
|
fun MutableMethod.addInstruction(
|
|
index: Int,
|
|
smaliInstructions: String,
|
|
) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
|
|
|
/**
|
|
* Add an instruction to a method.
|
|
*
|
|
* @param smaliInstructions The instruction to add.
|
|
*/
|
|
fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
|
|
|
/**
|
|
* Add instructions to a method at the given index.
|
|
*
|
|
* @param index The index to add the instructions at.
|
|
* @param instructions The instructions to add.
|
|
*/
|
|
fun MutableMethod.addInstructions(
|
|
index: Int,
|
|
instructions: List<BuilderInstruction>,
|
|
) = implementation!!.addInstructions(index, instructions)
|
|
|
|
/**
|
|
* Add instructions to a method.
|
|
*
|
|
* @param instructions The instructions to add.
|
|
*/
|
|
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) = implementation!!.addInstructions(instructions)
|
|
|
|
/**
|
|
* Add instructions to a method.
|
|
*
|
|
* @param smaliInstructions The instructions to add.
|
|
*/
|
|
fun MutableMethod.addInstructions(
|
|
index: Int,
|
|
smaliInstructions: String,
|
|
) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
|
|
|
/**
|
|
* Add instructions to a method.
|
|
*
|
|
* @param smaliInstructions The instructions to add.
|
|
*/
|
|
fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
|
|
|
/**
|
|
* Add instructions to a method at the given index.
|
|
*
|
|
* @param index The index to add the instructions at.
|
|
* @param smaliInstructions The instructions to add.
|
|
* @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions].
|
|
*/
|
|
// Special function for adding instructions with external labels.
|
|
fun MutableMethod.addInstructionsWithLabels(
|
|
index: Int,
|
|
smaliInstructions: String,
|
|
vararg externalLabels: ExternalLabel,
|
|
) {
|
|
// Create reference dummy instructions for the instructions.
|
|
val nopSmali =
|
|
StringBuilder(smaliInstructions).also { builder ->
|
|
externalLabels.forEach { (name, _) ->
|
|
builder.append("\n:$name\nnop")
|
|
}
|
|
}.toString()
|
|
|
|
// Compile the instructions with the dummy labels
|
|
val compiledInstructions = nopSmali.toInstructions(this)
|
|
|
|
// Add the compiled list of instructions to the method.
|
|
addInstructions(
|
|
index,
|
|
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size),
|
|
)
|
|
|
|
implementation!!.apply {
|
|
this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size)
|
|
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
|
|
// If the compiled instruction is not an offset instruction, skip it.
|
|
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
|
|
|
/**
|
|
* Create a new label for the instruction
|
|
* and replace it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
|
*/
|
|
fun Instruction.makeNewLabel() {
|
|
fun replaceOffset(
|
|
i: BuilderOffsetInstruction,
|
|
label: Label,
|
|
): BuilderOffsetInstruction {
|
|
return when (i) {
|
|
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
|
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
|
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
|
is BuilderInstruction22t ->
|
|
BuilderInstruction22t(
|
|
i.opcode,
|
|
i.registerA,
|
|
i.registerB,
|
|
label,
|
|
)
|
|
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
|
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
|
else -> throw IllegalStateException(
|
|
"A non-offset instruction was given, this should never happen!",
|
|
)
|
|
}
|
|
}
|
|
|
|
// Create the final label.
|
|
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
|
|
|
// Create the final instruction with the new label.
|
|
val newInstruction =
|
|
replaceOffset(
|
|
compiledInstruction,
|
|
label,
|
|
)
|
|
|
|
// Replace the instruction pointing to the dummy label
|
|
// with the new instruction pointing to the real instruction.
|
|
replaceInstruction(index + compiledInstructionIndex, newInstruction)
|
|
}
|
|
|
|
// If the compiled instruction targets its own instruction,
|
|
// which means it points to some of its own, simply an offset has to be applied.
|
|
val labelIndex = compiledInstruction.target.location.index
|
|
if (labelIndex < compiledInstructions.size - externalLabels.size) {
|
|
// Get the targets index (insertion index + the index of the dummy instruction).
|
|
this.instructions[index + labelIndex].makeNewLabel()
|
|
return@forEachIndexed
|
|
}
|
|
|
|
// Since the compiled instruction points to a dummy instruction,
|
|
// we can find the real instruction which it was created for by calculation.
|
|
|
|
// Get the index of the instruction in the externalLabels list
|
|
// which the dummy instruction was created for.
|
|
// This works because we created the dummy instructions in the same order as the externalLabels list.
|
|
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
|
|
instruction.makeNewLabel()
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove an instruction at the given index.
|
|
*
|
|
* @param index The index to remove the instruction at.
|
|
*/
|
|
fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index)
|
|
|
|
/**
|
|
* Remove instructions at the given index.
|
|
*
|
|
* @param index The index to remove the instructions at.
|
|
* @param count The amount of instructions to remove.
|
|
*/
|
|
fun MutableMethod.removeInstructions(
|
|
index: Int,
|
|
count: Int,
|
|
) = implementation!!.removeInstructions(index, count)
|
|
|
|
/**
|
|
* Remove instructions at the given index.
|
|
*
|
|
* @param count The amount of instructions to remove.
|
|
*/
|
|
fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count)
|
|
|
|
/**
|
|
* Replace an instruction at the given index.
|
|
*
|
|
* @param index The index to replace the instruction at.
|
|
* @param instruction The instruction to replace the instruction with.
|
|
*/
|
|
fun MutableMethod.replaceInstruction(
|
|
index: Int,
|
|
instruction: BuilderInstruction,
|
|
) = implementation!!.replaceInstruction(index, instruction)
|
|
|
|
/**
|
|
* Replace an instruction at the given index.
|
|
*
|
|
* @param index The index to replace the instruction at.
|
|
* @param smaliInstruction The smali instruction to replace the instruction with.
|
|
*/
|
|
fun MutableMethod.replaceInstruction(
|
|
index: Int,
|
|
smaliInstruction: String,
|
|
) = implementation!!.replaceInstructions(index, smaliInstruction.toInstructions(this))
|
|
|
|
/**
|
|
* Replace instructions at the given index.
|
|
*
|
|
* @param index The index to replace the instructions at.
|
|
* @param instructions The instructions to replace the instructions with.
|
|
*/
|
|
fun MutableMethod.replaceInstructions(
|
|
index: Int,
|
|
instructions: List<BuilderInstruction>,
|
|
) = implementation!!.replaceInstructions(index, instructions)
|
|
|
|
/**
|
|
* Replace instructions at the given index.
|
|
*
|
|
* @param index The index to replace the instructions at.
|
|
* @param smaliInstructions The smali instructions to replace the instructions with.
|
|
*/
|
|
fun MutableMethod.replaceInstructions(
|
|
index: Int,
|
|
smaliInstructions: String,
|
|
) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
*
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction.
|
|
*/
|
|
fun MethodImplementation.getInstruction(index: Int) = instructions.elementAt(index)
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
*
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction.
|
|
*/
|
|
@Suppress("UNCHECKED_CAST")
|
|
fun <T> MethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
*
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction.
|
|
*/
|
|
fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index]
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
*
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction.
|
|
*/
|
|
@Suppress("UNCHECKED_CAST")
|
|
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction or null if the method has no implementation.
|
|
*/
|
|
fun Method.getInstructionOrNull(index: Int): Instruction? = implementation?.getInstruction(index)
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction.
|
|
*/
|
|
fun Method.getInstruction(index: Int): Instruction = getInstructionOrNull(index)!!
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction or null if the method has no implementation.
|
|
*/
|
|
fun <T> Method.getInstructionOrNull(index: Int): T? = implementation?.getInstruction<T>(index)
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction.
|
|
*/
|
|
fun <T> Method.getInstruction(index: Int): T = getInstructionOrNull<T>(index)!!
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction or null if the method has no implementation.
|
|
*/
|
|
fun MutableMethod.getInstructionOrNull(index: Int): BuilderInstruction? = implementation?.getInstruction(index)
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @return The instruction.
|
|
*/
|
|
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = getInstructionOrNull(index)!!
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction or null if the method has no implementation.
|
|
*/
|
|
fun <T> MutableMethod.getInstructionOrNull(index: Int): T? = implementation?.getInstruction<T>(index)
|
|
|
|
/**
|
|
* Get an instruction at the given index.
|
|
* @param index The index to get the instruction at.
|
|
* @param T The type of instruction to return.
|
|
* @return The instruction.
|
|
*/
|
|
fun <T> MutableMethod.getInstruction(index: Int): T = getInstructionOrNull<T>(index)!!
|
|
|
|
/**
|
|
* The instructions of a method.
|
|
* @return The instructions or null if the method has no implementation.
|
|
*/
|
|
val Method.instructionsOrNull: Iterable<Instruction>? get() = implementation?.instructions
|
|
|
|
/**
|
|
* The instructions of a method.
|
|
* @return The instructions.
|
|
*/
|
|
val Method.instructions: Iterable<Instruction> get() = instructionsOrNull!!
|
|
|
|
/**
|
|
* The instructions of a method.
|
|
* @return The instructions or null if the method has no implementation.
|
|
*/
|
|
val MutableMethod.instructionsOrNull: MutableList<BuilderInstruction>? get() = implementation?.instructions
|
|
|
|
/**
|
|
* The instructions of a method.
|
|
* @return The instructions.
|
|
*/
|
|
val MutableMethod.instructions: MutableList<BuilderInstruction> get() = instructionsOrNull!!
|
|
|
|
/**
|
|
* Create a label for the instruction at given index.
|
|
*
|
|
* @param index The index to create the label for the instruction at.
|
|
* @return The label.
|
|
*/
|
|
fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index)
|
|
|
|
/**
|
|
* A class that represents a label for an instruction.
|
|
* @param name The label name.
|
|
* @param instruction The instruction that this label is for.
|
|
*/
|
|
data class ExternalLabel(internal val name: String, internal val instruction: Instruction)
|