forked from openrs2/openrs2
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.
159 lines
5.0 KiB
159 lines
5.0 KiB
package dev.openrs2.deob.ast.transform
|
|
|
|
import com.github.javaparser.ast.CompilationUnit
|
|
import com.github.javaparser.ast.Node
|
|
import com.github.javaparser.ast.stmt.BlockStmt
|
|
import com.github.javaparser.ast.stmt.IfStmt
|
|
import com.github.javaparser.ast.stmt.Statement
|
|
import dev.openrs2.deob.ast.util.countNots
|
|
import dev.openrs2.deob.ast.util.not
|
|
import dev.openrs2.deob.ast.util.walk
|
|
|
|
class IfElseTransformer : Transformer() {
|
|
override fun transform(unit: CompilationUnit) {
|
|
unit.walk(Node.TreeTraversal.POSTORDER) { stmt: IfStmt ->
|
|
stmt.elseStmt.ifPresent { elseStmt: Statement ->
|
|
val condition = stmt.condition
|
|
val thenStmt = stmt.thenStmt
|
|
if (isIf(thenStmt) && !isIf(elseStmt)) {
|
|
stmt.condition = condition.not()
|
|
stmt.thenStmt = elseStmt.clone()
|
|
stmt.setElseStmt(thenStmt.clone())
|
|
} else if (!isIf(thenStmt) && isIf(elseStmt)) {
|
|
/*
|
|
* Don't consider any more conditions for swapping the
|
|
* if/else branches, as it'll introduce another level of
|
|
* indentation.
|
|
*/
|
|
return@ifPresent
|
|
}
|
|
|
|
// Prefer fewer NOTs in the if condition
|
|
val notCondition = condition.not()
|
|
if (notCondition.countNots() < condition.countNots()) {
|
|
stmt.condition = notCondition
|
|
if (elseStmt.isIfStmt) {
|
|
val block = BlockStmt()
|
|
block.statements.add(elseStmt.clone())
|
|
stmt.thenStmt = block
|
|
} else {
|
|
stmt.thenStmt = elseStmt.clone()
|
|
}
|
|
stmt.setElseStmt(thenStmt.clone())
|
|
}
|
|
}
|
|
}
|
|
|
|
unit.walk(Node.TreeTraversal.POSTORDER) { stmt: IfStmt ->
|
|
stmt.elseStmt.ifPresent { elseStmt ->
|
|
if (isIf(elseStmt)) {
|
|
stmt.setElseStmt(getIf(elseStmt))
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Rewrite:
|
|
*
|
|
* ...
|
|
* } else {
|
|
* if (x != 123) {
|
|
* ...
|
|
* throw ...;
|
|
* }
|
|
* ...
|
|
* }
|
|
*
|
|
* to:
|
|
*
|
|
* ...
|
|
* } else if (x == 123) {
|
|
* ...
|
|
* } else {
|
|
* ...
|
|
* throw ...;
|
|
* }
|
|
*/
|
|
unit.walk(Node.TreeTraversal.POSTORDER) { stmt: IfStmt ->
|
|
stmt.elseStmt.ifPresent { elseStmt ->
|
|
// match
|
|
if (!elseStmt.isBlockStmt) {
|
|
return@ifPresent
|
|
}
|
|
|
|
val blockStmt = elseStmt.asBlockStmt()
|
|
val statements = blockStmt.statements
|
|
if (statements.isEmpty()) {
|
|
return@ifPresent
|
|
}
|
|
|
|
val head = statements[0]
|
|
if (!head.isIfStmt) {
|
|
return@ifPresent
|
|
}
|
|
|
|
val ifStmt = head.asIfStmt()
|
|
if (ifStmt.elseStmt.isPresent) {
|
|
return@ifPresent
|
|
}
|
|
|
|
val thenStmt = ifStmt.thenStmt
|
|
if (!isTailThrowOrReturn(thenStmt)) {
|
|
return@ifPresent
|
|
}
|
|
|
|
// rewrite
|
|
val condition = ifStmt.condition.not()
|
|
|
|
val tail = blockStmt.clone()
|
|
tail.statements.removeAt(0)
|
|
|
|
elseStmt.replace(IfStmt(condition, tail, thenStmt.clone()))
|
|
}
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private fun isIf(stmt: Statement): Boolean {
|
|
return when {
|
|
stmt.isIfStmt -> true
|
|
stmt.isBlockStmt -> {
|
|
val stmts = stmt.asBlockStmt().statements
|
|
stmts.size == 1 && stmts[0].isIfStmt
|
|
}
|
|
else -> false
|
|
}
|
|
}
|
|
|
|
private fun getIf(stmt: Statement): Statement {
|
|
if (stmt.isIfStmt) {
|
|
return stmt.clone()
|
|
} else if (stmt.isBlockStmt) {
|
|
val stmts = stmt.asBlockStmt().statements
|
|
if (stmts.size == 1) {
|
|
val head = stmts[0]
|
|
if (head.isIfStmt) {
|
|
return head.clone()
|
|
}
|
|
}
|
|
}
|
|
throw IllegalArgumentException()
|
|
}
|
|
|
|
private fun isTailThrowOrReturn(stmt: Statement): Boolean {
|
|
return if (stmt.isThrowStmt || stmt.isReturnStmt) {
|
|
true
|
|
} else if (stmt.isBlockStmt) {
|
|
val stmts = stmt.asBlockStmt().statements
|
|
if (stmts.isEmpty()) {
|
|
return false
|
|
}
|
|
|
|
val tail = stmts[stmts.size - 1]
|
|
tail.isThrowStmt || tail.isReturnStmt
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|