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.
335 lines
7.9 KiB
335 lines
7.9 KiB
package dev.openrs2.asm;
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.io.UncheckedIOException;
|
|
|
|
import org.objectweb.asm.Opcodes;
|
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
|
import org.objectweb.asm.tree.InsnNode;
|
|
import org.objectweb.asm.tree.IntInsnNode;
|
|
import org.objectweb.asm.tree.LdcInsnNode;
|
|
import org.objectweb.asm.util.Textifier;
|
|
import org.objectweb.asm.util.TraceMethodVisitor;
|
|
|
|
public final class InsnNodeUtils {
|
|
public static AbstractInsnNode nextReal(AbstractInsnNode insn) {
|
|
while ((insn = insn.getNext()) != null && insn.getOpcode() == -1) {
|
|
/* empty */
|
|
}
|
|
return insn;
|
|
}
|
|
|
|
public static AbstractInsnNode previousReal(AbstractInsnNode insn) {
|
|
while ((insn = insn.getPrevious()) != null && insn.getOpcode() == -1) {
|
|
/* empty */
|
|
}
|
|
return insn;
|
|
}
|
|
|
|
public static AbstractInsnNode nextVirtual(AbstractInsnNode insn) {
|
|
while ((insn = insn.getNext()) != null && insn.getOpcode() != -1) {
|
|
/* empty */
|
|
}
|
|
return insn;
|
|
}
|
|
|
|
public static AbstractInsnNode previousVirtual(AbstractInsnNode insn) {
|
|
while ((insn = insn.getPrevious()) != null && insn.getOpcode() != -1) {
|
|
/* empty */
|
|
}
|
|
return insn;
|
|
}
|
|
|
|
public static boolean isIntConstant(AbstractInsnNode insn) {
|
|
switch (insn.getOpcode()) {
|
|
case Opcodes.ICONST_M1:
|
|
case Opcodes.ICONST_0:
|
|
case Opcodes.ICONST_1:
|
|
case Opcodes.ICONST_2:
|
|
case Opcodes.ICONST_3:
|
|
case Opcodes.ICONST_4:
|
|
case Opcodes.ICONST_5:
|
|
case Opcodes.BIPUSH:
|
|
case Opcodes.SIPUSH:
|
|
return true;
|
|
case Opcodes.LDC:
|
|
var ldc = (LdcInsnNode) insn;
|
|
return ldc.cst instanceof Integer;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static int getIntConstant(AbstractInsnNode insn) {
|
|
switch (insn.getOpcode()) {
|
|
case Opcodes.ICONST_M1:
|
|
return -1;
|
|
case Opcodes.ICONST_0:
|
|
return 0;
|
|
case Opcodes.ICONST_1:
|
|
return 1;
|
|
case Opcodes.ICONST_2:
|
|
return 2;
|
|
case Opcodes.ICONST_3:
|
|
return 3;
|
|
case Opcodes.ICONST_4:
|
|
return 4;
|
|
case Opcodes.ICONST_5:
|
|
return 5;
|
|
case Opcodes.BIPUSH:
|
|
case Opcodes.SIPUSH:
|
|
var intInsn = (IntInsnNode) insn;
|
|
return intInsn.operand;
|
|
case Opcodes.LDC:
|
|
var ldc = (LdcInsnNode) insn;
|
|
if (ldc.cst instanceof Integer) {
|
|
return (Integer) ldc.cst;
|
|
}
|
|
}
|
|
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
public static AbstractInsnNode createIntConstant(int value) {
|
|
switch (value) {
|
|
case -1:
|
|
return new InsnNode(Opcodes.ICONST_M1);
|
|
case 0:
|
|
return new InsnNode(Opcodes.ICONST_0);
|
|
case 1:
|
|
return new InsnNode(Opcodes.ICONST_1);
|
|
case 2:
|
|
return new InsnNode(Opcodes.ICONST_2);
|
|
case 3:
|
|
return new InsnNode(Opcodes.ICONST_3);
|
|
case 4:
|
|
return new InsnNode(Opcodes.ICONST_4);
|
|
case 5:
|
|
return new InsnNode(Opcodes.ICONST_5);
|
|
}
|
|
|
|
if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
|
return new IntInsnNode(Opcodes.BIPUSH, value);
|
|
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
|
return new IntInsnNode(Opcodes.SIPUSH, value);
|
|
} else {
|
|
return new LdcInsnNode(value);
|
|
}
|
|
}
|
|
|
|
public static boolean hasSideEffects(AbstractInsnNode insn) {
|
|
var opcode = insn.getOpcode();
|
|
switch (opcode) {
|
|
case -1:
|
|
case Opcodes.NOP:
|
|
case Opcodes.ACONST_NULL:
|
|
case Opcodes.ICONST_M1:
|
|
case Opcodes.ICONST_0:
|
|
case Opcodes.ICONST_1:
|
|
case Opcodes.ICONST_2:
|
|
case Opcodes.ICONST_3:
|
|
case Opcodes.ICONST_4:
|
|
case Opcodes.ICONST_5:
|
|
case Opcodes.LCONST_0:
|
|
case Opcodes.LCONST_1:
|
|
case Opcodes.FCONST_0:
|
|
case Opcodes.FCONST_1:
|
|
case Opcodes.FCONST_2:
|
|
case Opcodes.DCONST_0:
|
|
case Opcodes.DCONST_1:
|
|
case Opcodes.BIPUSH:
|
|
case Opcodes.SIPUSH:
|
|
case Opcodes.LDC:
|
|
case Opcodes.ILOAD:
|
|
case Opcodes.LLOAD:
|
|
case Opcodes.FLOAD:
|
|
case Opcodes.DLOAD:
|
|
case Opcodes.ALOAD:
|
|
return false;
|
|
case Opcodes.IALOAD:
|
|
case Opcodes.LALOAD:
|
|
case Opcodes.FALOAD:
|
|
case Opcodes.DALOAD:
|
|
case Opcodes.AALOAD:
|
|
case Opcodes.BALOAD:
|
|
case Opcodes.CALOAD:
|
|
case Opcodes.SALOAD:
|
|
/* might throw NPE or index out of bounds exception */
|
|
case Opcodes.ISTORE:
|
|
case Opcodes.LSTORE:
|
|
case Opcodes.FSTORE:
|
|
case Opcodes.DSTORE:
|
|
case Opcodes.ASTORE:
|
|
case Opcodes.IASTORE:
|
|
case Opcodes.LASTORE:
|
|
case Opcodes.FASTORE:
|
|
case Opcodes.DASTORE:
|
|
case Opcodes.AASTORE:
|
|
case Opcodes.BASTORE:
|
|
case Opcodes.CASTORE:
|
|
case Opcodes.SASTORE:
|
|
return true;
|
|
case Opcodes.POP:
|
|
case Opcodes.POP2:
|
|
case Opcodes.DUP:
|
|
case Opcodes.DUP_X1:
|
|
case Opcodes.DUP_X2:
|
|
case Opcodes.DUP2:
|
|
case Opcodes.DUP2_X1:
|
|
case Opcodes.DUP2_X2:
|
|
case Opcodes.SWAP:
|
|
case Opcodes.IADD:
|
|
case Opcodes.LADD:
|
|
case Opcodes.FADD:
|
|
case Opcodes.DADD:
|
|
case Opcodes.ISUB:
|
|
case Opcodes.LSUB:
|
|
case Opcodes.FSUB:
|
|
case Opcodes.DSUB:
|
|
case Opcodes.IMUL:
|
|
case Opcodes.LMUL:
|
|
case Opcodes.FMUL:
|
|
case Opcodes.DMUL:
|
|
case Opcodes.IDIV:
|
|
case Opcodes.LDIV:
|
|
case Opcodes.FDIV:
|
|
case Opcodes.DDIV:
|
|
case Opcodes.IREM:
|
|
case Opcodes.LREM:
|
|
case Opcodes.FREM:
|
|
case Opcodes.DREM:
|
|
/*
|
|
* XXX(gpe): strictly speaking the *DIV and *REM instructions have
|
|
* side effects (unless we can prove that the second argument is
|
|
* non-zero). However, treating them as having side effects reduces
|
|
* the number of dummy variables we can remove, so we pretend they
|
|
* don't have any side effects.
|
|
*
|
|
* This doesn't seem to cause any problems with the client, as it
|
|
* doesn't deliberately try to trigger divide-by-zero exceptions.
|
|
*/
|
|
case Opcodes.INEG:
|
|
case Opcodes.LNEG:
|
|
case Opcodes.FNEG:
|
|
case Opcodes.DNEG:
|
|
case Opcodes.ISHL:
|
|
case Opcodes.LSHL:
|
|
case Opcodes.ISHR:
|
|
case Opcodes.LSHR:
|
|
case Opcodes.IUSHR:
|
|
case Opcodes.LUSHR:
|
|
case Opcodes.IAND:
|
|
case Opcodes.LAND:
|
|
case Opcodes.IOR:
|
|
case Opcodes.LOR:
|
|
case Opcodes.IXOR:
|
|
case Opcodes.LXOR:
|
|
return false;
|
|
case Opcodes.IINC:
|
|
return true;
|
|
case Opcodes.I2L:
|
|
case Opcodes.I2F:
|
|
case Opcodes.I2D:
|
|
case Opcodes.L2I:
|
|
case Opcodes.L2F:
|
|
case Opcodes.L2D:
|
|
case Opcodes.F2I:
|
|
case Opcodes.F2L:
|
|
case Opcodes.F2D:
|
|
case Opcodes.D2I:
|
|
case Opcodes.D2L:
|
|
case Opcodes.D2F:
|
|
case Opcodes.I2B:
|
|
case Opcodes.I2C:
|
|
case Opcodes.I2S:
|
|
case Opcodes.LCMP:
|
|
case Opcodes.FCMPL:
|
|
case Opcodes.FCMPG:
|
|
case Opcodes.DCMPL:
|
|
case Opcodes.DCMPG:
|
|
return false;
|
|
case Opcodes.IFEQ:
|
|
case Opcodes.IFNE:
|
|
case Opcodes.IFLT:
|
|
case Opcodes.IFGE:
|
|
case Opcodes.IFGT:
|
|
case Opcodes.IFLE:
|
|
case Opcodes.IF_ICMPEQ:
|
|
case Opcodes.IF_ICMPNE:
|
|
case Opcodes.IF_ICMPLT:
|
|
case Opcodes.IF_ICMPGE:
|
|
case Opcodes.IF_ICMPGT:
|
|
case Opcodes.IF_ICMPLE:
|
|
case Opcodes.IF_ACMPEQ:
|
|
case Opcodes.IF_ACMPNE:
|
|
case Opcodes.GOTO:
|
|
case Opcodes.JSR:
|
|
case Opcodes.RET:
|
|
case Opcodes.TABLESWITCH:
|
|
case Opcodes.LOOKUPSWITCH:
|
|
case Opcodes.IRETURN:
|
|
case Opcodes.LRETURN:
|
|
case Opcodes.FRETURN:
|
|
case Opcodes.DRETURN:
|
|
case Opcodes.ARETURN:
|
|
case Opcodes.RETURN:
|
|
return true;
|
|
case Opcodes.GETSTATIC:
|
|
return false;
|
|
case Opcodes.PUTSTATIC:
|
|
case Opcodes.GETFIELD:
|
|
/* might throw NPE */
|
|
case Opcodes.PUTFIELD:
|
|
return true;
|
|
case Opcodes.INVOKEVIRTUAL:
|
|
case Opcodes.INVOKESPECIAL:
|
|
case Opcodes.INVOKESTATIC:
|
|
case Opcodes.INVOKEINTERFACE:
|
|
case Opcodes.INVOKEDYNAMIC:
|
|
return true;
|
|
case Opcodes.NEW:
|
|
return false;
|
|
case Opcodes.NEWARRAY:
|
|
case Opcodes.ANEWARRAY:
|
|
/* arrays might be created with negative size */
|
|
case Opcodes.ARRAYLENGTH:
|
|
/* might throw NPE */
|
|
case Opcodes.ATHROW:
|
|
case Opcodes.CHECKCAST:
|
|
return true;
|
|
case Opcodes.INSTANCEOF:
|
|
return false;
|
|
case Opcodes.MONITORENTER:
|
|
case Opcodes.MONITOREXIT:
|
|
case Opcodes.MULTIANEWARRAY:
|
|
case Opcodes.IFNULL:
|
|
case Opcodes.IFNONNULL:
|
|
return true;
|
|
}
|
|
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
public static String toString(AbstractInsnNode insn) {
|
|
var printer = new Textifier();
|
|
|
|
var visitor = new TraceMethodVisitor(printer);
|
|
insn.accept(visitor);
|
|
|
|
try (
|
|
var stringWriter = new StringWriter();
|
|
var printWriter = new PrintWriter(stringWriter)
|
|
) {
|
|
printer.print(printWriter);
|
|
return stringWriter.toString().trim();
|
|
} catch (IOException ex) {
|
|
throw new UncheckedIOException(ex);
|
|
}
|
|
}
|
|
|
|
private InsnNodeUtils() {
|
|
/* empty */
|
|
}
|
|
}
|
|
|