diff --git a/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.java b/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.java deleted file mode 100644 index b2f306a1cf..0000000000 --- a/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.java +++ /dev/null @@ -1,173 +0,0 @@ -package dev.openrs2.asm; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.FrameNode; -import org.objectweb.asm.tree.IincInsnNode; -import org.objectweb.asm.tree.LabelNode; -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.objectweb.asm.tree.analysis.BasicInterpreter; - -public final class MethodNodeUtils { - private static int localIndex(int access, List argTypes, int argIndex) { - var localIndex = 0; - if ((access & Opcodes.ACC_STATIC) == 0) { - localIndex++; - } - for (int i = 0; i < argIndex; i++) { - localIndex += argTypes.get(i).getSize(); - } - return localIndex; - } - - private static int remap(int i, Type argType, int localIndex) { - if (i >= localIndex) { - return i - argType.getSize(); - } - return i; - } - - private static List remapAll(List indexes, Type argType, int localIndex) { - return indexes.stream() - .map(i -> remap(i, argType, localIndex)) - .collect(Collectors.toList()); - } - - public static void removeArgument(MethodNode method, int argIndex) { - /* remove argument from the signature */ - var type = Type.getType(method.desc); - - var argTypes = new ArrayList<>(Arrays.asList(type.getArgumentTypes())); - var argType = argTypes.remove(argIndex); - - method.desc = Type.getMethodDescriptor(type.getReturnType(), argTypes.toArray(Type[]::new)); - - if (method.signature != null) { - throw new UnsupportedOperationException("Signatures unsupported"); - } - - /* calculate index of the local we're removing */ - var localIndex = localIndex(method.access, argTypes, argIndex); - - /* remove ParameterNode */ - if (method.parameters != null) { - method.parameters.remove(argIndex); - } - - /* remove annotations */ - if (method.visibleAnnotableParameterCount != 0) { - throw new UnsupportedOperationException("Non-zero visibleAnnotableParameterCount unsupported"); - } - - if (method.visibleParameterAnnotations != null) { - @SuppressWarnings("unchecked") - var annotations = (List[]) new List[method.visibleParameterAnnotations.length - 1]; - System.arraycopy(method.visibleParameterAnnotations, 0, annotations, 0, argIndex); - System.arraycopy(method.visibleParameterAnnotations, argIndex + 1, annotations, argIndex, method.visibleParameterAnnotations.length - argIndex - 1); - method.visibleParameterAnnotations = annotations; - } - - if (method.invisibleAnnotableParameterCount != 0) { - throw new UnsupportedOperationException("Non-zero invisibleAnnotableParameterCount unsupported"); - } - - if (method.invisibleParameterAnnotations != null) { - @SuppressWarnings("unchecked") - var annotations = (List[]) new List[method.invisibleParameterAnnotations.length - 1]; - System.arraycopy(method.invisibleParameterAnnotations, 0, annotations, 0, argIndex); - System.arraycopy(method.invisibleParameterAnnotations, argIndex + 1, annotations, argIndex, method.invisibleParameterAnnotations.length - argIndex - 1); - method.invisibleParameterAnnotations = annotations; - } - - /* reduce maxLocals */ - method.maxLocals -= argType.getSize(); - - /* remap locals */ - if (method.localVariables != null) { - method.localVariables.removeIf(v -> v.index == localIndex); - method.localVariables.forEach(v -> v.index = remap(v.index, argType, localIndex)); - } - - if (method.visibleLocalVariableAnnotations != null) { - method.visibleLocalVariableAnnotations.removeIf(v -> v.index.contains(localIndex)); - method.visibleLocalVariableAnnotations.forEach(v -> v.index = remapAll(v.index, argType, localIndex)); - } - - if (method.invisibleLocalVariableAnnotations != null) { - method.invisibleLocalVariableAnnotations.removeIf(v -> v.index.contains(localIndex)); - method.invisibleLocalVariableAnnotations.forEach(v -> v.index = remapAll(v.index, argType, localIndex)); - } - - for (var insn : method.instructions) { - switch (insn.getType()) { - case AbstractInsnNode.VAR_INSN: - var varInsn = (VarInsnNode) insn; - varInsn.var = remap(varInsn.var, argType, localIndex); - break; - case AbstractInsnNode.IINC_INSN: - var iincInsn = (IincInsnNode) insn; - iincInsn.var = remap(iincInsn.var, argType, localIndex); - break; - case AbstractInsnNode.FRAME: - var frame = (FrameNode) insn; - if (frame.type != Opcodes.F_NEW) { - throw new UnsupportedOperationException("Only F_NEW frames are supported"); - } - - for (int i = 0; i < argType.getSize(); i++) { - frame.local.remove(localIndex); - } - break; - } - } - } - - public static void removeDeadCode(String owner, MethodNode method) throws AnalyzerException { - boolean changed; - do { - changed = false; - - var analyzer = new Analyzer<>(new BasicInterpreter()); - var frames = analyzer.analyze(owner, method); - - var deadLabels = new HashSet(); - var i = 0; - for (var it = method.instructions.iterator(); it.hasNext(); ) { - var insn = it.next(); - if (frames[i++] != null) { - continue; - } - - if (insn.getType() == AbstractInsnNode.LABEL) { - deadLabels.add((LabelNode) insn); - } else { - it.remove(); - changed = true; - } - } - - if (method.tryCatchBlocks.removeIf(tryCatch -> deadLabels.contains(tryCatch.start) && deadLabels.contains(tryCatch.end))) { - changed = true; - } - } while (changed); - } - - public static boolean hasCode(MethodNode method) { - return (method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0; - } - - private MethodNodeUtils() { - /* empty */ - } -} diff --git a/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.kt b/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.kt new file mode 100644 index 0000000000..f83c50e233 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/MethodNodeUtils.kt @@ -0,0 +1,134 @@ +package dev.openrs2.asm + +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 +import org.objectweb.asm.tree.analysis.BasicInterpreter +import org.objectweb.asm.tree.analysis.BasicValue + +private fun localIndex(access: Int, argTypes: Array, argIndex: Int): Int { + var localIndex = 0 + if (access and Opcodes.ACC_STATIC == 0) { + localIndex++ + } + for (i in 0 until argIndex) { + localIndex += argTypes[i].size + } + return localIndex +} + +private fun remap(i: Int, argType: Type, localIndex: Int): Int { + return if (i >= localIndex) { + i - argType.size + } else { + i + } +} + +private fun remapAll(indexes: List, argType: Type, localIndex: Int): MutableList { + return indexes.map { remap(it, argType, localIndex) }.toMutableList() +} + +fun MethodNode.removeArgument(argIndex: Int) { + // remove argument from the descriptor + val type = Type.getType(desc) + val argType = type.argumentTypes[argIndex] + val argTypes = type.argumentTypes.filterIndexed { index, _ -> index != argIndex }.toTypedArray() + desc = Type.getMethodDescriptor(type.returnType, *argTypes) + + // the client doesn't use signatures so don't bother with them + if (signature != null) { + throw UnsupportedOperationException("Signatures unsupported") + } + + parameters?.removeAt(argIndex) + + // remove annotations + if (visibleAnnotableParameterCount != 0) { + throw UnsupportedOperationException("Non-zero visibleAnnotableParameterCount unsupported") + } + + if (visibleParameterAnnotations != null) { + visibleParameterAnnotations = + visibleParameterAnnotations.filterIndexed { index, _ -> index != argIndex }.toTypedArray() + } + + if (invisibleAnnotableParameterCount != 0) { + throw UnsupportedOperationException("Non-zero invisibleAnnotableParameterCount unsupported") + } + + if (invisibleParameterAnnotations != null) { + invisibleParameterAnnotations = + invisibleParameterAnnotations.filterIndexed { index, _ -> index != argIndex }.toTypedArray() + } + + // remap locals + val localIndex = localIndex(access, argTypes, argIndex) + maxLocals -= argType.size + + if (localVariables != null) { + localVariables.removeIf { it.index == localIndex } + localVariables.forEach { it.index = remap(it.index, argType, localIndex) } + } + + if (visibleLocalVariableAnnotations != null) { + visibleLocalVariableAnnotations.removeIf { it.index.contains(localIndex) } + visibleLocalVariableAnnotations.forEach { it.index = remapAll(it.index, argType, localIndex) } + } + + if (invisibleLocalVariableAnnotations != null) { + invisibleLocalVariableAnnotations.removeIf { it.index.contains(localIndex) } + invisibleLocalVariableAnnotations.forEach { it.index = remapAll(it.index, argType, localIndex) } + } + + for (insn in instructions) { + when (insn) { + is VarInsnNode -> insn.`var` = remap(insn.`var`, argType, localIndex) + is IincInsnNode -> insn.`var` = remap(insn.`var`, argType, localIndex) + is FrameNode -> { + if (insn.type != Opcodes.F_NEW) { + throw UnsupportedOperationException("Only F_NEW frames are supported") + } + + for (i in 0 until argType.size) { + insn.local.removeAt(localIndex) + } + } + } + } +} + +@Throws(AnalyzerException::class) +fun MethodNode.removeDeadCode(owner: String) { + var changed: Boolean + do { + changed = false + + val analyzer = Analyzer(BasicInterpreter()) + val frames = analyzer.analyze(owner, this) + + val deadLabels = mutableSetOf() + val it = instructions.iterator() + var i = 0 + for (insn in it) { + if (frames[i++] != null) { + continue + } + + if (insn is LabelNode) { + deadLabels.add(insn) + } else { + it.remove() + changed = true + } + } + + changed = changed or tryCatchBlocks.removeIf { deadLabels.contains(it.start) && deadLabels.contains(it.end) } + } while (changed) +} + +fun MethodNode.hasCode(): Boolean { + return access and (Opcodes.ACC_NATIVE or Opcodes.ACC_ABSTRACT) == 0 +} diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/Library.java b/asm/src/main/java/dev/openrs2/asm/classpath/Library.java index d2218ca87d..c6d0e04e30 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/Library.java +++ b/asm/src/main/java/dev/openrs2/asm/classpath/Library.java @@ -15,7 +15,7 @@ import java.util.jar.JarOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.remap.ClassForNameRemapper; import dev.openrs2.util.io.DeterministicJarOutputStream; import dev.openrs2.util.io.SkipOutputStream; @@ -119,7 +119,7 @@ public final class Library implements Iterable { for (var clazz : classes.values()) { for (var method : clazz.methods) { - if (MethodNodeUtils.hasCode(method)) { + if (MethodNodeUtilsKt.hasCode(method)) { ClassForNameRemapper.remap(remapper, method); } } diff --git a/asm/src/main/java/dev/openrs2/asm/transform/Transformer.kt b/asm/src/main/java/dev/openrs2/asm/transform/Transformer.kt index 733e874732..c81b68f2c4 100644 --- a/asm/src/main/java/dev/openrs2/asm/transform/Transformer.kt +++ b/asm/src/main/java/dev/openrs2/asm/transform/Transformer.kt @@ -1,8 +1,8 @@ package dev.openrs2.asm.transform -import dev.openrs2.asm.MethodNodeUtils.hasCode import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.hasCode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode import org.objectweb.asm.tree.MethodNode @@ -29,7 +29,7 @@ abstract class Transformer { for (method in clazz.methods) { changed = changed or preTransformMethod(classPath, library, clazz, method) - if (hasCode(method)) { + if (method.hasCode()) { changed = changed or transformCode(classPath, library, clazz, method) } diff --git a/bundler/src/main/java/dev/openrs2/bundler/transform/BufferSizeTransformer.java b/bundler/src/main/java/dev/openrs2/bundler/transform/BufferSizeTransformer.java index 743dff82b8..f0f62e927e 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/transform/BufferSizeTransformer.java +++ b/bundler/src/main/java/dev/openrs2/bundler/transform/BufferSizeTransformer.java @@ -5,7 +5,7 @@ import java.util.Optional; import dev.openrs2.asm.InsnMatcher; import dev.openrs2.asm.InsnNodeUtils; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -46,7 +46,7 @@ public final class BufferSizeTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (!MethodNodeUtils.hasCode(method)) { + if (!MethodNodeUtilsKt.hasCode(method)) { continue; } diff --git a/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java index 70e767d36c..d66b3910b2 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java +++ b/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java @@ -5,7 +5,7 @@ import java.util.Map; import dev.openrs2.asm.InsnMatcher; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -35,7 +35,7 @@ public final class BitwiseOpTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (!MethodNodeUtils.hasCode(method)) { + if (!MethodNodeUtilsKt.hasCode(method)) { continue; } diff --git a/deob/src/main/java/dev/openrs2/deob/transform/CounterTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/CounterTransformer.java index 34a2127041..dd14e8cb8c 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/CounterTransformer.java +++ b/deob/src/main/java/dev/openrs2/deob/transform/CounterTransformer.java @@ -7,7 +7,7 @@ import java.util.Set; import dev.openrs2.asm.InsnMatcher; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -37,7 +37,7 @@ public final class CounterTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (MethodNodeUtils.hasCode(method)) { + if (MethodNodeUtilsKt.hasCode(method)) { findCounters(method, references, resets, increments); } } diff --git a/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java index 4f714df3ff..d70aefe438 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java +++ b/deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java @@ -6,7 +6,7 @@ import java.util.Set; import dev.openrs2.asm.InsnMatcher; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -39,7 +39,7 @@ public final class OpaquePredicateTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (MethodNodeUtils.hasCode(method)) { + if (MethodNodeUtilsKt.hasCode(method)) { findFlowObstructors(library, method); } } diff --git a/deob/src/main/java/dev/openrs2/deob/transform/ResetTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/ResetTransformer.java index c4394da7f1..ff51a6abc5 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/ResetTransformer.java +++ b/deob/src/main/java/dev/openrs2/deob/transform/ResetTransformer.java @@ -5,7 +5,7 @@ import java.util.Set; import dev.openrs2.asm.InsnNodeUtils; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -66,7 +66,7 @@ public final class ResetTransformer extends Transformer { } private static void findResetMethods(Set resetMethods, ClassNode clazz, MethodNode method) throws AnalyzerException { - MethodNodeUtils.removeDeadCode(clazz.name, method); + MethodNodeUtilsKt.removeDeadCode(method, clazz.name); for (var insn : method.instructions) { if (insn.getOpcode() == -1 || insn.getOpcode() != Opcodes.INVOKESTATIC) { @@ -87,7 +87,7 @@ public final class ResetTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (!MethodNodeUtils.hasCode(method)) { + if (!MethodNodeUtilsKt.hasCode(method)) { continue; } diff --git a/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java index 97e52dc3a9..ab8cd2f40d 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java +++ b/deob/src/main/java/dev/openrs2/deob/transform/UnusedArgTransformer.java @@ -8,7 +8,7 @@ import java.util.Set; import com.google.common.collect.ImmutableSet; import dev.openrs2.asm.MemberRef; -import dev.openrs2.asm.MethodNodeUtils; +import dev.openrs2.asm.MethodNodeUtilsKt; import dev.openrs2.asm.classpath.ClassPath; import dev.openrs2.asm.classpath.Library; import dev.openrs2.asm.transform.Transformer; @@ -66,7 +66,7 @@ public final class UnusedArgTransformer extends Transformer { for (var library : classPath.getLibraries()) { for (var clazz : library) { for (var method : clazz.methods) { - if (MethodNodeUtils.hasCode(method)) { + if (MethodNodeUtilsKt.hasCode(method)) { populateRetainedArgs(classPath, clazz, method); } } @@ -123,7 +123,7 @@ public final class UnusedArgTransformer extends Transformer { @Override protected boolean preTransformMethod(ClassPath classPath, Library library, ClassNode clazz, MethodNode method) throws AnalyzerException { /* delete unused int args from call sites */ - if (MethodNodeUtils.hasCode(method)) { + if (MethodNodeUtilsKt.hasCode(method)) { var analyzer = new Analyzer<>(new ConstSourceInterpreter()); var frames = analyzer.analyze(clazz.name, method); var deadInsns = new ArrayList(); @@ -185,7 +185,7 @@ public final class UnusedArgTransformer extends Transformer { 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))) { - MethodNodeUtils.removeArgument(method, i); + MethodNodeUtilsKt.removeArgument(method, i); deletedArgs++; } }