/*******************************************************************************************************************
* Authors: SanAndreasP
* Copyright: SanAndreasP
* License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
* http://creativecommons.org/licenses/by-nc-sa/4.0/
*******************************************************************************************************************/
package de.sanandrew.core.manpack.transformer;
import org.objectweb.asm.tree.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public final class InstructionComparator
{
public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static InsnList getImportantList(InsnList list) {
if( list.size() == 0 ) {
return list;
}
HashMap<LabelNode, LabelNode> labels = new HashMap<>();
for( AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext() ) {
if( insn instanceof LabelNode ) {
labels.put((LabelNode) insn, (LabelNode) insn);
}
}
InsnList importantNodeList = new InsnList();
for( AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext() ) {
if( insn instanceof LabelNode || insn instanceof LineNumberNode ) {
continue;
}
importantNodeList.add(insn.clone(labels));
}
return importantNodeList;
}
public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2) {
return node1.var == node2.var && node1.incr == node2.incr;
}
public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2) {
if( node1.getType() != node2.getType() ) {
return false;
} else if( node1.getOpcode() != node2.getOpcode() ) {
return false;
}
switch( node2.getType() ) {
case VAR_INSN:
return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2);
case TYPE_INSN:
return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2);
case FIELD_INSN:
return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2);
case METHOD_INSN:
return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2);
case LDC_INSN:
return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2);
case IINC_INSN:
return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2);
case INT_INSN:
return intInsnEqual((IntInsnNode) node1, (IntInsnNode) node2);
default:
return true;
}
}
public static List<Integer> insnListFind(InsnList haystack, InsnList needle) {
LinkedList<Integer> list = new LinkedList<>();
for( int start = 0; start <= haystack.size() - needle.size(); start++ ) {
if( insnListMatches(haystack, needle, start) ) {
list.add(start);
}
}
return list;
}
public static List<AbstractInsnNode> insnListFindEnd(InsnList haystack, InsnList needle) {
LinkedList<AbstractInsnNode> callNodes = new LinkedList<>();
for( int callPoint : insnListFind(haystack, needle) ) {
callNodes.add(haystack.get(callPoint + needle.size() - 1));
}
return callNodes;
}
public static List<InsnListSection> insnListFindL(InsnList haystack, InsnList needle) {
HashSet<LabelNode> controlFlowLabels = new HashSet<>();
for( AbstractInsnNode insn = haystack.getFirst(); insn != null; insn = insn.getNext() ) {
switch( insn.getType() ) {
case 8:
case 15:
break;
case 7:
JumpInsnNode jinsn = (JumpInsnNode) insn;
controlFlowLabels.add(jinsn.label);
break;
case 11:
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode) insn;
for( LabelNode label : tsinsn.labels ) {
controlFlowLabels.add(label);
}
break;
case 12:
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode) insn;
for( LabelNode label : lsinsn.labels ) {
controlFlowLabels.add(label);
}
break;
}
}
LinkedList<InsnListSection> list = new LinkedList<>();
nextsection:
for( int start = 0; start <= haystack.size() - needle.size(); start++ ) {
InsnListSection section = insnListMatchesL(haystack, needle, start, controlFlowLabels);
if( section != null ) {
for( InsnListSection asection : list ) {
if( asection.last == section.last ) {
continue nextsection;
}
}
list.add(section);
}
}
return list;
}
public static List<AbstractInsnNode> insnListFindStart(InsnList haystack, InsnList needle) {
LinkedList<AbstractInsnNode> callNodes = new LinkedList<>();
for( int callPoint : insnListFind(haystack, needle) ) {
callNodes.add(haystack.get(callPoint));
}
return callNodes;
}
public static boolean insnListMatches(InsnList haystack, InsnList needle, int start) {
if( haystack.size() - start < needle.size() ) {
return false;
}
for( int i = 0; i < needle.size(); i++ ) {
if( !insnEqual(haystack.get(i + start), needle.get(i)) ) {
return false;
}
}
return true;
}
private static InsnListSection insnListMatchesL(InsnList haystack, InsnList needle, int start,
HashSet<LabelNode> controlFlowLabels) {
int h = start, n = 0;
for(; h < haystack.size() && n < needle.size(); h++ ) {
AbstractInsnNode insn = haystack.get(h);
if( insn.getType() == 15 ) {
continue;
}
if( insn.getType() == 8 && !controlFlowLabels.contains(insn) ) {
continue;
}
if( !insnEqual(haystack.get(h), needle.get(n)) ) {
return null;
}
n++;
}
if( n != needle.size() ) {
return null;
}
return new InsnListSection(haystack, start, h - 1);
}
public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2) {
return node1.operand == -1 || node2.operand == -1 || node1.operand == node2.operand;
}
public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2) {
return insn1.cst.equals("~") || insn2.cst.equals("~") || insn1.cst.equals(insn2.cst);
}
public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2) {
return insn1.desc.equals("~") || insn2.desc.equals("~") || insn1.desc.equals(insn2.desc);
}
public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2) {
return insn1.var == -1 || insn2.var == -1 || insn1.var == insn2.var;
}
public static class InsnListSection
{
public AbstractInsnNode first;
public AbstractInsnNode last;
public InsnListSection(InsnList haystack, int start, int end) {
this.first = haystack.get(start);
this.last = haystack.get(end);
}
}
}