diff --git a/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java deleted file mode 100644 index 445c7ec1..00000000 --- a/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java +++ /dev/null @@ -1,200 +0,0 @@ -package dev.openrs2.deob.transform; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.ImmutableSet; -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 dev.openrs2.common.collect.DisjointSet; -import dev.openrs2.deob.ArgRef; -import dev.openrs2.deob.analysis.ConstSourceInterpreter; -import dev.openrs2.deob.remap.TypedRemapper; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; -import org.objectweb.asm.tree.analysis.Analyzer; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class UnusedArgTransformer extends Transformer { - private static final Logger logger = LoggerFactory.getLogger(UnusedArgTransformer.class); - - private static final ImmutableSet INT_SORTS = ImmutableSet.of(Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.INT, Type.CHAR); - - private static Map createLocalToArgMap(MethodNode method) { - var type = Type.getType(method.desc); - var argumentTypes = type.getArgumentTypes(); - - var map = new HashMap(); - var argIndex = 0; - var localIndex = 0; - - if ((method.access & Opcodes.ACC_STATIC) == 0) { - localIndex++; - } - - for (var t : argumentTypes) { - map.put(localIndex, argIndex++); - localIndex += t.getSize(); - } - - return map; - } - - private final Set retainedArgs = new HashSet<>(); - private DisjointSet inheritedMethodSets; - private int deletedArgs; - - @Override - protected void preTransform(ClassPath classPath) throws AnalyzerException { - retainedArgs.clear(); - inheritedMethodSets = classPath.createInheritedMethodSets(); - deletedArgs = 0; - - for (var library : classPath.getLibraries()) { - for (var clazz : library) { - for (var method : clazz.methods) { - if (MethodNodeUtilsKt.hasCode(method)) { - populateRetainedArgs(classPath, clazz, method); - } - } - } - } - } - - private void populateRetainedArgs(ClassPath classPath, ClassNode clazz, MethodNode method) throws AnalyzerException { - var partition = inheritedMethodSets.get(new MemberRef(clazz, method)); - var localToArgMap = createLocalToArgMap(method); - - var analyzer = new Analyzer<>(new ConstSourceInterpreter()); - var frames = analyzer.analyze(clazz.name, method); - - for (var i = 0; i < frames.length; i++) { - var frame = frames[i]; - if (frame == null) { - continue; - } - - var stackSize = frame.getStackSize(); - - var insn = method.instructions.get(i); - switch (insn.getOpcode()) { - case Opcodes.ILOAD: - var iload = (VarInsnNode) insn; - var arg = localToArgMap.get(iload.var); - if (arg != null) { - retainedArgs.add(new ArgRef(partition, arg)); - } - break; - case Opcodes.INVOKEVIRTUAL: - case Opcodes.INVOKESPECIAL: - case Opcodes.INVOKESTATIC: - case Opcodes.INVOKEINTERFACE: - var invoke = (MethodInsnNode) insn; - var invokePartition = inheritedMethodSets.get(new MemberRef(invoke)); - if (invokePartition == null || TypedRemapper.isMethodImmutable(classPath, invokePartition)) { - continue; - } - - var args = Type.getArgumentTypes(invoke.desc).length; - for (var j = 0; j < args; j++) { - var source = frame.getStack(stackSize - args + j); - if (!source.isSingleSourceConstant()) { - retainedArgs.add(new ArgRef(invokePartition, j)); - } - } - break; - } - } - } - - @Override - protected boolean preTransformMethod(ClassPath classPath, Library library, ClassNode clazz, MethodNode method) throws AnalyzerException { - /* delete unused int args from call sites */ - if (MethodNodeUtilsKt.hasCode(method)) { - var analyzer = new Analyzer<>(new ConstSourceInterpreter()); - var frames = analyzer.analyze(clazz.name, method); - var deadInsns = new ArrayList(); - - for (var i = 0; i < frames.length; i++) { - var frame = frames[i]; - if (frame == null) { - continue; - } - - var stackSize = frame.getStackSize(); - - var insn = method.instructions.get(i); - if (insn.getType() != AbstractInsnNode.METHOD_INSN) { - continue; - } - - var methodInsn = (MethodInsnNode) insn; - var partition = inheritedMethodSets.get(new MemberRef(methodInsn)); - if (partition == null || TypedRemapper.isMethodImmutable(classPath, partition)) { - continue; - } - - var type = Type.getType(methodInsn.desc); - var argTypes = type.getArgumentTypes(); - - var newArgTypes = new ArrayList(); - - for (var j = 0; j < argTypes.length; j++) { - var argType = argTypes[j]; - if (INT_SORTS.contains(argType.getSort()) && !retainedArgs.contains(new ArgRef(partition, j))) { - var source = frame.getStack(stackSize - argTypes.length + j).getSource(); - deadInsns.add(source); - } else { - newArgTypes.add(argType); - } - } - - methodInsn.desc = Type.getMethodDescriptor(type.getReturnType(), newArgTypes.toArray(Type[]::new)); - } - - deadInsns.forEach(method.instructions::remove); - } - - return false; - } - - @Override - protected boolean postTransformMethod(ClassPath classPath, Library library, ClassNode clazz, MethodNode method) { - /* delete unused int args from the method itself */ - var partition = inheritedMethodSets.get(new MemberRef(clazz, method)); - if (TypedRemapper.isMethodImmutable(classPath, partition)) { - return false; - } - - var type = Type.getType(method.desc); - var argTypes = type.getArgumentTypes(); - - for (var i = argTypes.length - 1; i >= 0; i--) { - var argType = argTypes[i]; - if (INT_SORTS.contains(argType.getSort()) && !retainedArgs.contains(new ArgRef(partition, i))) { - MethodNodeUtilsKt.removeArgument(method, i); - deletedArgs++; - } - } - - return false; - } - - @Override - protected void postTransform(ClassPath classPath) { - logger.info("Removed {} dummy arguments", deletedArgs); - } -} diff --git a/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.kt b/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.kt new file mode 100644 index 00000000..05ce21a4 --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.kt @@ -0,0 +1,196 @@ +package dev.openrs2.deob.transform + +import com.github.michaelbull.logging.InlineLogger +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.removeArgument +import dev.openrs2.asm.transform.Transformer +import dev.openrs2.common.collect.DisjointSet +import dev.openrs2.deob.ArgRef +import dev.openrs2.deob.analysis.ConstSourceInterpreter +import dev.openrs2.deob.remap.TypedRemapper +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.tree.* +import org.objectweb.asm.tree.analysis.Analyzer +import org.objectweb.asm.tree.analysis.AnalyzerException + +class UnusedArgTransformer : Transformer() { + private val retainedArgs = mutableSetOf() + private lateinit var inheritedMethodSets: DisjointSet + private var deletedArgs = 0 + + @Throws(AnalyzerException::class) + override fun preTransform(classPath: ClassPath) { + retainedArgs.clear() + inheritedMethodSets = classPath.createInheritedMethodSets() + deletedArgs = 0 + + for (library in classPath.libraries) { + for (clazz in library) { + for (method in clazz.methods) { + if (method.hasCode()) { + populateRetainedArgs(classPath, clazz, method) + } + } + } + } + } + + @Throws(AnalyzerException::class) + private fun populateRetainedArgs(classPath: ClassPath, clazz: ClassNode, method: MethodNode) { + val partition = inheritedMethodSets[MemberRef(clazz, method)]!! + val localToArgMap = createLocalToArgMap(method) + + val analyzer = Analyzer(ConstSourceInterpreter()) + val frames = analyzer.analyze(clazz.name, method) + + frame@ for ((i, frame) in frames.withIndex()) { + if (frame == null) { + continue + } + + val stackSize = frame.stackSize + + val insn = method.instructions[i] + when (insn) { + is VarInsnNode -> { + if (insn.opcode != Opcodes.ILOAD) { + continue@frame + } + + val arg = localToArgMap[insn.`var`] + if (arg != null) { + retainedArgs.add(ArgRef(partition, arg)) + } + } + is MethodInsnNode -> { + val invokePartition = inheritedMethodSets[MemberRef(insn)] + if (invokePartition == null || TypedRemapper.isMethodImmutable(classPath, invokePartition)) { + continue@frame + } + + val args = Type.getArgumentTypes(insn.desc).size + for (j in 0 until args) { + val source = frame.getStack(stackSize - args + j) + if (!source.isSingleSourceConstant) { + retainedArgs.add(ArgRef(invokePartition, j)) + } + } + } + } + } + } + + @Throws(AnalyzerException::class) + override fun preTransformMethod( + classPath: ClassPath, + library: Library, + clazz: ClassNode, + method: MethodNode + ): Boolean { + // delete unused int args from call sites + if (!method.hasCode()) { + return false + } + + val analyzer = Analyzer(ConstSourceInterpreter()) + val frames = analyzer.analyze(clazz.name, method) + val deadInsns = mutableListOf() + + for ((i, frame) in frames.withIndex()) { + if (frame == null) { + continue + } + + val stackSize = frame.stackSize + + val insn = method.instructions[i] + if (insn !is MethodInsnNode) { + continue + } + + val partition = inheritedMethodSets[MemberRef(insn)] + if (partition == null || TypedRemapper.isMethodImmutable(classPath, partition)) { + continue + } + + val type = Type.getType(insn.desc) + val argTypes = type.argumentTypes + val newArgTypes = mutableListOf() + + for ((j, argType) in argTypes.withIndex()) { + if (INT_SORTS.contains(argType.sort) && !retainedArgs.contains(ArgRef(partition, j))) { + val source = frame.getStack(stackSize - argTypes.size + j).source + deadInsns.add(source) + } else { + newArgTypes.add(argType) + } + } + + insn.desc = Type.getMethodDescriptor(type.returnType, *newArgTypes.toTypedArray()) + } + + deadInsns.forEach(method.instructions::remove) + + return false + } + + override fun postTransformMethod( + classPath: ClassPath, + library: Library, + clazz: ClassNode, + method: MethodNode + ): Boolean { + // delete unused int args from the method itself + val partition = inheritedMethodSets[MemberRef(clazz, method)]!! + if (TypedRemapper.isMethodImmutable(classPath, partition)) { + return false + } + + val argTypes = Type.getType(method.desc).argumentTypes + for ((i, argType) in argTypes.withIndex().reversed()) { + if (INT_SORTS.contains(argType.sort) && !retainedArgs.contains(ArgRef(partition, i))) { + method.removeArgument(i) + deletedArgs++ + } + } + + return false + } + + override fun postTransform(classPath: ClassPath) { + logger.info { "Removed $deletedArgs dummy arguments" } + } + + companion object { + private val logger = InlineLogger() + private val INT_SORTS = setOf( + Type.BOOLEAN, + Type.BYTE, + Type.SHORT, + Type.INT, + Type.CHAR + ) + + private fun createLocalToArgMap(method: MethodNode): Map { + val argTypes = Type.getType(method.desc).argumentTypes + + val map = mutableMapOf() + var localIndex = 0 + + if (method.access and Opcodes.ACC_STATIC == 0) { + localIndex++ + } + + for ((argIndex, t) in argTypes.withIndex()) { + map[localIndex] = argIndex + localIndex += t.size + } + + return map + } + } +}