Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
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.
136 lines
4.6 KiB
136 lines
4.6 KiB
5 years ago
|
package dev.openrs2.asm;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Arrays;
|
||
|
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.MethodNode;
|
||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||
|
|
||
|
public final class MethodNodeUtils {
|
||
|
private static int localIndex(int access, List<Type> 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<Integer> remapAll(List<Integer> indexes, Type argType, int localIndex) {
|
||
|
return indexes.stream()
|
||
|
.map(i -> remap(i, argType, localIndex))
|
||
|
.collect(Collectors.toList());
|
||
|
}
|
||
|
|
||
|
public static void deleteArgument(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<AnnotationNode>[]) 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<AnnotationNode>[]) 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 it = method.instructions.iterator(); it.hasNext(); ) {
|
||
|
var insn = it.next();
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private MethodNodeUtils() {
|
||
|
/* empty */
|
||
|
}
|
||
|
}
|