refactor: improve the addInstructions extension method further more

This commit is contained in:
oSumAtrIX 2022-07-09 06:26:05 +02:00
parent 6bfe5716c3
commit dd941233ca
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
4 changed files with 88 additions and 59 deletions

View file

@ -2,6 +2,7 @@ package app.revanced.patcher.extensions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import org.jf.dexlib2.AccessFlags
@ -11,6 +12,7 @@ import org.jf.dexlib2.builder.Label
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.*
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.reference.MethodReference
import org.jf.dexlib2.immutable.ImmutableMethod
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
@ -50,8 +52,10 @@ fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
* @return True if the methods match given the conditions.
*/
fun Method.softCompareTo(otherMethod: MethodReference): Boolean {
if (MethodUtil.isConstructor(this) && !parametersEqual(this.parameterTypes, otherMethod.parameterTypes))
return false
if (MethodUtil.isConstructor(this) && !parametersEqual(
this.parameterTypes, otherMethod.parameterTypes
)
) return false
return this.name == otherMethod.name
}
@ -71,14 +75,7 @@ internal fun Method.clone(registerCount: Int = 0): ImmutableMethod {
)
}
return ImmutableMethod(
returnType,
name,
parameters,
returnType,
accessFlags,
annotations,
hiddenApiRestrictions,
clonedImplementation
returnType, name, parameters, returnType, accessFlags, annotations, hiddenApiRestrictions, clonedImplementation
)
}
@ -109,46 +106,88 @@ fun MutableMethod.replaceInstruction(index: Int, instruction: String) =
* Remove a smali instruction within the method.
* @param index The index to delete the instruction at.
*/
fun MutableMethod.removeInstruction(index: Int) =
this.implementation!!.removeInstruction(index)
fun MutableMethod.removeInstruction(index: Int) = this.implementation!!.removeInstruction(index)
/**
* Create a label for the instruction at given index in the method's implementation.
* @param index The index to create the label for the instruction at.
* @return The label.
*/
fun MutableMethod.label(index: Int) = this.implementation!!.newLabelForIndex(index)
/**
* Get the instruction at given index in the method's implementation.
* @param index The index to get the instruction at.
* @return The instruction.
*/
fun MutableMethod.instruction(index: Int): BuilderInstruction = this.implementation!!.instructions[index]
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param smali The smali instructions to add.
* @param externalLabels A list of [ExternalLabel] representing a list of labels for instructions which are not in the method to compile.
*/
fun MutableMethod.addInstructions(index: Int, smali: String, labels: List<Pair<String, Label>> = emptyList()) {
var code = smali
for ((name, _) in labels) {
code += "\n :$name \n nop"
}
val instructions = code.toInstructions(this).toMutableList()
var fixedInstructions: List<Int>? = null // find a better way to do this.
if (labels.isNotEmpty()) {
val labelRange = instructions.size - labels.size..instructions.size
fixedInstructions = mutableListOf()
for (instructionIndex in 0 until instructions.size - labels.size) {
val instruction = instructions[instructionIndex]
if (instruction !is BuilderOffsetInstruction || !instruction.target.isPlaced) continue
val fakeIndex = instruction.target.location.index
if (!labelRange.contains(fakeIndex)) continue
instructions[instructionIndex] = replaceOffset(instruction, labels[labelRange.indexOf(fakeIndex)].second)
fixedInstructions.add(instructionIndex + index)
fun MutableMethod.addInstructions(index: Int, smali: String, externalLabels: List<ExternalLabel> = emptyList()) {
// create reference dummy instructions for the instructions.
val nopedSmali = StringBuilder(smali).also { builder ->
externalLabels.forEach { (name, _) ->
builder.append("\n:$name\nnop")
}
// find a better way to drop the nop instructions.
instructions.subList(labelRange.first, labelRange.last).clear()
}
}.toString()
this.implementation!!.addInstructions(index, instructions)
this.fixInstructions(index, instructions, fixedInstructions)
// compile the instructions with the dummy labels
val compiledInstructions = nopedSmali.toInstructions(this)
// add the compiled list of instructions to the method.
val methodImplementation = this.implementation!!
methodImplementation.addInstructions(index, compiledInstructions)
val methodInstructions = methodImplementation.instructions
methodInstructions.subList(index, index + compiledInstructions.size)
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
// If the compiled instruction is not an offset instruction, skip it.
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
/**
* Creates a new label for the instruction and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
*/
fun Instruction.makeNewLabel() {
// create the final label.
val label = methodImplementation.newLabelForIndex(methodInstructions.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.
methodImplementation.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).
methodInstructions[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 line works because we created the dummy instructions in the same order as the externalLabels list.
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
instruction.makeNewLabel()
}
}
/**
* Add smali instructions to the end of the method.
* @param instructions The smali instructions to add.
*/
fun MutableMethod.addInstructions(instructions: String, labels: List<Pair<String, Label>> = emptyList()) =
fun MutableMethod.addInstructions(instructions: String, labels: List<ExternalLabel> = emptyList()) =
this.addInstructions(this.implementation!!.instructions.size, instructions, labels)
/**
@ -164,27 +203,10 @@ fun MutableMethod.replaceInstructions(index: Int, instructions: String) =
* @param index The index to remove the instructions at.
* @param count The amount of instructions to remove.
*/
fun MutableMethod.removeInstructions(index: Int, count: Int) =
this.implementation!!.removeInstructions(index, count)
fun MutableMethod.label(index: Int) = this.implementation!!.newLabelForIndex(index)
fun MutableMethod.instruction(index: Int): BuilderInstruction = this.implementation!!.instructions[index]
private fun MutableMethod.fixInstructions(index: Int, instructions: List<BuilderInstruction>, skipInstructions: List<Int>?) {
for (instructionIndex in index until instructions.size + index) {
val instruction = this.implementation!!.instructions[instructionIndex]
if (instruction !is BuilderOffsetInstruction || !instruction.target.isPlaced) continue
if (skipInstructions?.contains(instructionIndex) == true) continue
val fakeIndex = instruction.target.location.index
val fixedIndex = fakeIndex + index
if (fakeIndex == fixedIndex) continue // no need to replace if the indexes are the same.
this.implementation!!.replaceInstruction(instructionIndex, replaceOffset(instruction, this.label(fixedIndex)))
}
}
fun MutableMethod.removeInstructions(index: Int, count: Int) = this.implementation!!.removeInstructions(index, count)
private fun replaceOffset(
i: BuilderOffsetInstruction,
label: Label
i: BuilderOffsetInstruction, label: Label
): BuilderOffsetInstruction {
return when (i) {
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
@ -207,8 +229,7 @@ internal fun Method.cloneMutable(registerCount: Int = 0) = clone(registerCount).
// FIXME: also check the order of parameters as different order equals different method overload
internal fun parametersEqual(
parameters1: Iterable<CharSequence>,
parameters2: Iterable<CharSequence>
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
): Boolean {
return parameters1.count() == parameters2.count() && parameters1.all { parameter ->
parameters2.any {

View file

@ -0,0 +1,10 @@
package app.revanced.patcher.util.smali
import org.jf.dexlib2.iface.instruction.Instruction
/**
* 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)

View file

@ -45,7 +45,6 @@ class InlineSmaliCompiler {
val tokens = CommonTokenStream(lexer as TokenSource)
val parser = smaliParser(tokens)
val result = parser.smali_file()
result.tree
if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) {
throw IllegalStateException(
"Encountered ${parser.numberOfSyntaxErrors} parser syntax errors and ${lexer.numberOfSyntaxErrors} lexer syntax errors!"

View file

@ -57,7 +57,6 @@ internal class InlineSmaliCompilerTest {
method.addInstructions(
"""
const/4 v0, 0x1
:test
const/4 v0, 0x0
"""
)
@ -70,7 +69,7 @@ internal class InlineSmaliCompilerTest {
if-eqz v0, :test
return-void
""", listOf(
"test" to method.label(labelIndex)
ExternalLabel("test",method.instruction(1))
)
)