diff --git a/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java deleted file mode 100644 index d70aefe4..00000000 --- a/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java +++ /dev/null @@ -1,125 +0,0 @@ -package dev.openrs2.deob.transform; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import dev.openrs2.asm.InsnMatcher; -import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtilsKt; -import dev.openrs2.asm.classpath.ClassPath; -import dev.openrs2.asm.classpath.Library; -import dev.openrs2.asm.transform.Transformer; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.JumpInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class OpaquePredicateTransformer extends Transformer { - private static final Logger logger = LoggerFactory.getLogger(OpaquePredicateTransformer.class); - - private static final InsnMatcher FLOW_OBSTRUCTOR_INITIALIZER_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) IFEQ (((GETSTATIC ISTORE)? IINC ILOAD) | ((GETSTATIC | ILOAD) IFEQ ICONST GOTO ICONST)) PUTSTATIC"); - private static final InsnMatcher OPAQUE_PREDICATE_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) (IFEQ | IFNE)"); - private static final InsnMatcher STORE_MATCHER = InsnMatcher.compile("GETSTATIC ISTORE"); - - private final Set flowObstructors = new HashSet<>(); - private int opaquePredicates, stores; - - @Override - public void preTransform(ClassPath classPath) { - flowObstructors.clear(); - opaquePredicates = 0; - stores = 0; - - for (var library : classPath.getLibraries()) { - for (var clazz : library) { - for (var method : clazz.methods) { - if (MethodNodeUtilsKt.hasCode(method)) { - findFlowObstructors(library, method); - } - } - } - } - - logger.info("Identified flow obstructors {}", flowObstructors); - } - - private void findFlowObstructors(Library library, MethodNode method) { - FLOW_OBSTRUCTOR_INITIALIZER_MATCHER.match(method).forEach(match -> { - /* add flow obstructor to set */ - var putstatic = (FieldInsnNode) match.get(match.size() - 1); - flowObstructors.add(new MemberRef(putstatic)); - - /* remove initializer */ - match.forEach(method.instructions::remove); - - /* remove field */ - var owner = library.get(putstatic.owner); - owner.fields.removeIf(field -> field.name.equals(putstatic.name) && field.desc.equals(putstatic.desc)); - }); - } - - private boolean isFlowObstructor(FieldInsnNode insn) { - return flowObstructors.contains(new MemberRef(insn)); - } - - private boolean isOpaquePredicate(MethodNode method, List match) { - var load = match.get(0); - - /* flow obstructor loaded directly? */ - if (load.getOpcode() == Opcodes.GETSTATIC) { - var getstatic = (FieldInsnNode) load; - return isFlowObstructor(getstatic); - } - - /* flow obstructor loaded via local variable? */ - var iload = (VarInsnNode) load; - return STORE_MATCHER.match(method).anyMatch(storeMatch -> { - var getstatic = (FieldInsnNode) storeMatch.get(0); - var istore = (VarInsnNode) storeMatch.get(1); - return isFlowObstructor(getstatic) && iload.var == istore.var; - }); - } - - private boolean isRedundantStore(List match) { - var getstatic = (FieldInsnNode) match.get(0); - return isFlowObstructor(getstatic); - } - - @Override - public boolean transformCode(ClassPath classPath, Library library, ClassNode clazz, MethodNode method) { - /* find and fix opaque predicates */ - OPAQUE_PREDICATE_MATCHER.match(method).filter(match -> isOpaquePredicate(method, match)).forEach(match -> { - var branch = (JumpInsnNode) match.get(1); - - if (branch.getOpcode() == Opcodes.IFEQ) { - /* branch is always taken */ - method.instructions.remove(match.get(0)); - branch.setOpcode(Opcodes.GOTO); - } else { /* IFNE */ - /* branch is never taken */ - match.forEach(method.instructions::remove); - } - - opaquePredicates++; - }); - - /* remove redundant stores */ - STORE_MATCHER.match(method).filter(this::isRedundantStore).forEach(match -> { - match.forEach(method.instructions::remove); - stores++; - }); - - return false; - } - - @Override - public void postTransform(ClassPath classPath) { - logger.info("Removed {} opaque predicates and {} redundant stores", opaquePredicates, stores); - } -} diff --git a/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.kt b/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.kt new file mode 100644 index 00000000..a056b1b6 --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.kt @@ -0,0 +1,113 @@ +package dev.openrs2.deob.transform + +import com.github.michaelbull.logging.InlineLogger +import dev.openrs2.asm.InsnMatcher +import dev.openrs2.asm.MemberRef +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.hasCode +import dev.openrs2.asm.transform.Transformer +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.* + +class OpaquePredicateTransformer : Transformer() { + private val flowObstructors = mutableSetOf() + private var opaquePredicates = 0 + private var stores = 0 + + override fun preTransform(classPath: ClassPath) { + flowObstructors.clear() + opaquePredicates = 0 + stores = 0 + + for (library in classPath.libraries) { + for (clazz in library) { + for (method in clazz.methods) { + if (method.hasCode()) { + findFlowObstructors(library, method) + } + } + } + } + + logger.info { "Identified flow obstructors $flowObstructors" } + } + + private fun findFlowObstructors(library: Library, method: MethodNode) { + FLOW_OBSTRUCTOR_INITIALIZER_MATCHER.match(method).forEach { + // add flow obstructor to set + val putstatic = it.last() as FieldInsnNode + flowObstructors.add(MemberRef(putstatic)) + + // remove initializer + it.forEach(method.instructions::remove) + + // remove field + val owner = library[putstatic.owner]!! + owner.fields.removeIf { it.name == putstatic.name && it.desc == putstatic.desc } + } + } + + private fun isFlowObstructor(insn: FieldInsnNode): Boolean { + return flowObstructors.contains(MemberRef(insn)) + } + + private fun isOpaquePredicate(method: MethodNode, match: List): Boolean { + val load = match[0] + + // flow obstructor loaded directly? + if (load is FieldInsnNode && load.opcode == Opcodes.GETSTATIC) { + return isFlowObstructor(load) + } + + // flow obstructor loaded via local variable + val iload = load as VarInsnNode + return STORE_MATCHER.match(method).anyMatch { + val getstatic = it[0] as FieldInsnNode + val istore = it[1] as VarInsnNode + return@anyMatch isFlowObstructor(getstatic) && iload.`var` == istore.`var` + } + } + + private fun isRedundantStore(match: List): Boolean { + val getstatic = match[0] as FieldInsnNode + return isFlowObstructor(getstatic) + } + + override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean { + // find and fix opaque predicates + OPAQUE_PREDICATE_MATCHER.match(method).filter { isOpaquePredicate(method, it) }.forEach { + val branch = it[1] as JumpInsnNode + if (branch.opcode == Opcodes.IFEQ) { + // branch is always taken + method.instructions.remove(it[0]) + branch.opcode = Opcodes.GOTO + } else { // IFNE + // branch is never taken + it.forEach(method.instructions::remove) + } + + opaquePredicates++ + } + + // remove redundant stores + STORE_MATCHER.match(method).filter(this::isRedundantStore).forEach { + it.forEach(method.instructions::remove) + stores++ + } + + return false + } + + override fun postTransform(classPath: ClassPath) { + logger.info { "Removed $opaquePredicates opaque predicates and $stores redundant stores" } + } + + companion object { + private val logger = InlineLogger() + private val FLOW_OBSTRUCTOR_INITIALIZER_MATCHER = + InsnMatcher.compile("(GETSTATIC | ILOAD) IFEQ (((GETSTATIC ISTORE)? IINC ILOAD) | ((GETSTATIC | ILOAD) IFEQ ICONST GOTO ICONST)) PUTSTATIC") + private val OPAQUE_PREDICATE_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) (IFEQ | IFNE)") + private val STORE_MATCHER = InsnMatcher.compile("GETSTATIC ISTORE") + } +}