Open-source multiplayer game server compatible with the RuneScape client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
openrs2/deob-ir/src/main/java/dev/openrs2/deob/ir/translation/decompiler/IrAnalyzer.kt

106 lines
3.2 KiB

package dev.openrs2.deob.ir.translation.decompiler
import com.google.common.graph.ValueGraphBuilder
import dev.openrs2.deob.ir.AssignmentStmt
import dev.openrs2.deob.ir.BasicBlock
import dev.openrs2.deob.ir.BasicBlockGraph
import dev.openrs2.deob.ir.BinaryExpr
import dev.openrs2.deob.ir.CallExpr
import dev.openrs2.deob.ir.CallStmt
import dev.openrs2.deob.ir.Expr
import dev.openrs2.deob.ir.IfStmt
import dev.openrs2.deob.ir.ReturnStmt
import dev.openrs2.deob.ir.StmtVisitor
import dev.openrs2.deob.ir.VarExpr
import dev.openrs2.deob.ir.flow.ControlFlowTransfer
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.JumpInsnNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.analysis.Analyzer
class IrAnalyzer(private val interpreter: IrInterpreter) : StmtVisitor, Analyzer<IrValue>(interpreter) {
init {
interpreter.visitor = this
}
/**
* A mapping of program counters (instruction offsets) to the [BasicBlock]s
* that define them.
*/
private lateinit var blocks: MutableMap<Int, BasicBlock>
/**
* The control flow graph defining the method.
*/
private lateinit var graph: BasicBlockGraph
/**
* The current [BasicBlock] of the method that is being analyzed.
*/
private lateinit var bb: BasicBlock
/**
* The code belonging to the current method being analyzed.
*/
private lateinit var code: InsnList
/**
* Get or create a new basic block that begins at the instruction with the given [index].
*/
private fun bb(index: Int) = blocks.computeIfAbsent(index) { BasicBlock(graph) }
/**
* Run the analyzer on the [method] owned by the class with name [owner] and return the [BasicBlock]
* that defines method entry.
*/
fun decode(owner: String, method: MethodNode): BasicBlock {
analyze(owner, method)
return blocks[0] ?: error("No entry BasicBlock found")
}
override fun init(owner: String, method: MethodNode) {
graph = ValueGraphBuilder.directed()
.allowsSelfLoops(true)
.build()
bb = BasicBlock(graph)
code = method.instructions
blocks = mutableMapOf(0 to bb)
}
override fun newControlFlowEdge(insnIndex: Int, successorIndex: Int) {
when (val insn = code[insnIndex]) {
is JumpInsnNode -> {
val transfer = if (insn.opcode == Opcodes.GOTO) {
ControlFlowTransfer.ConditionalJump(code[successorIndex] == insn.label)
} else {
ControlFlowTransfer.Goto
}
graph.putEdgeValue(bb, bb(successorIndex), transfer)
}
}
val startsBlock = blocks[insnIndex]
if (startsBlock != null) {
bb = startsBlock
}
}
override fun visitAssignmen(variable: VarExpr, value: Expr) {
bb.statements.add(AssignmentStmt(variable, value))
}
override fun visitCall(expr: CallExpr) {
bb.statements.add(CallStmt(expr))
}
override fun visitIf(conditional: BinaryExpr) {
bb.statements.add(IfStmt(conditional))
}
override fun visitReturn(value: Expr?) {
bb.statements.add(ReturnStmt(value))
}
}