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.
 
 
 
 
openrs2/asm/src/main/java/dev/openrs2/asm/InsnNodeUtils.java

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 */
}
}