forked from openrs2/openrs2
parent
862c79e31c
commit
db785e38d0
@ -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<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 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<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 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<LabelNode>(); |
|
||||||
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 */ |
|
||||||
} |
|
||||||
} |
|
@ -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<Type>, 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<Int>, argType: Type, localIndex: Int): MutableList<Int> { |
||||||
|
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<BasicValue>(BasicInterpreter()) |
||||||
|
val frames = analyzer.analyze(owner, this) |
||||||
|
|
||||||
|
val deadLabels = mutableSetOf<LabelNode>() |
||||||
|
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 |
||||||
|
} |
Loading…
Reference in new issue