package codechicken.lib.asm;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
/**
* A section of an InsnList, may become invalid if the insn list is modified
*/
public class InsnListSection implements Iterable<AbstractInsnNode> {
private class InsnListSectionIterator implements Iterator<AbstractInsnNode> {
int i = 0;
@Override
public boolean hasNext() {
return i < size();
}
@Override
public AbstractInsnNode next() {
return get(i++);
}
@Override
public void remove() {
InsnListSection.this.remove(--i);
}
}
public InsnList list;
public int start;
public int end;
public InsnListSection(InsnList list, int start, int end) {
this.list = list;
this.start = start;
this.end = end;
}
public InsnListSection(InsnList list, AbstractInsnNode first, AbstractInsnNode last) {
this(list, list.indexOf(first), list.indexOf(last) + 1);
}
public InsnListSection(InsnList list) {
this(list, 0, list.size());
}
public InsnListSection() {
this(new InsnList());
}
public void accept(MethodVisitor mv) {
for (AbstractInsnNode insn : this) {
insn.accept(mv);
}
}
public AbstractInsnNode getFirst() {
return size() == 0 ? null : list.get(start);
}
public AbstractInsnNode getLast() {
return size() == 0 ? null : list.get(end - 1);
}
public int size() {
return end - start;
}
public AbstractInsnNode get(int i) {
return list.get(start + i);
}
public void set(int i, AbstractInsnNode insn) {
list.set(get(i), insn);
}
public void remove(int i) {
list.remove(get(i));
end--;
}
public void replace(AbstractInsnNode location, AbstractInsnNode insn) {
list.set(location, insn);
}
public void add(AbstractInsnNode insn) {
list.add(insn);
end++;
}
public void insertBefore(InsnList insns) {
int s = insns.size();
if (this.list.size() == 0) {
list.insert(insns);
} else {
list.insertBefore(list.get(start), insns);
}
start += s;
end += s;
}
public void insert(InsnList insns) {
if (end == 0) {
list.insert(insns);
} else {
list.insert(list.get(end - 1), insns);
}
}
public void replace(InsnList insns) {
int s = insns.size();
remove();
insert(insns);
end = start + s;
}
public void remove() {
while (end != start) {
remove(0);
}
}
public void setLast(AbstractInsnNode last) {
end = list.indexOf(last) + 1;
}
public void setFirst(AbstractInsnNode first) {
start = list.indexOf(first);
}
public InsnListSection drop(int n) {
return slice(n, size());
}
public InsnListSection take(int n) {
return slice(0, n);
}
public InsnListSection slice(int start, int end) {
return new InsnListSection(list, this.start + start, this.start + end);
}
/**
* Removes leading and trailing labels and line number nodes that don't affect control flow
*
* @return this
*/
public InsnListSection trim(Set<LabelNode> controlFlowLabels) {
while (start < end && !InsnComparator.insnImportant(getFirst(), controlFlowLabels)) {
start++;
}
while (start < end && !InsnComparator.insnImportant(getLast(), controlFlowLabels)) {
end--;
}
return this;
}
public String toString() {
Textifier t = new Textifier();
accept(new TraceMethodVisitor(t));
StringWriter sw = new StringWriter();
t.print(new PrintWriter(sw));
return sw.toString();
}
public void println() {
System.out.println(toString());
}
public HashMap<LabelNode, LabelNode> identityLabelMap() {
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for (AbstractInsnNode insn : this) {
switch (insn.getType()) {
case LABEL:
labelMap.put((LabelNode) insn, (LabelNode) insn);
break;
case JUMP_INSN:
labelMap.put(((JumpInsnNode) insn).label, ((JumpInsnNode) insn).label);
break;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode linsn = (LookupSwitchInsnNode) insn;
labelMap.put(linsn.dflt, linsn.dflt);
for (LabelNode label : linsn.labels) {
labelMap.put(label, label);
}
break;
case TABLESWITCH_INSN:
TableSwitchInsnNode tinsn = (TableSwitchInsnNode) insn;
labelMap.put(tinsn.dflt, tinsn.dflt);
for (LabelNode label : tinsn.labels) {
labelMap.put(label, label);
}
break;
case FRAME:
FrameNode fnode = (FrameNode) insn;
if (fnode.local != null) {
for (Object o : fnode.local) {
if (o instanceof LabelNode) {
labelMap.put((LabelNode) o, (LabelNode) o);
}
}
}
if (fnode.stack != null) {
for (Object o : fnode.stack) {
if (o instanceof LabelNode) {
labelMap.put((LabelNode) o, (LabelNode) o);
}
}
}
break;
}
}
return labelMap;
}
public Map<LabelNode, LabelNode> cloneLabels() {
Map<LabelNode, LabelNode> labelMap = identityLabelMap();
for (Entry<LabelNode, LabelNode> entry : labelMap.entrySet()) {
entry.setValue(new LabelNode());
}
return labelMap;
}
public InsnListSection copy() {
return copy(cloneLabels());
}
public InsnListSection copy(Map<LabelNode, LabelNode> labelMap) {
InsnListSection copy = new InsnListSection();
for (AbstractInsnNode insn : this) {
copy.add(insn.clone(labelMap));
}
return copy;
}
@Override
public Iterator<AbstractInsnNode> iterator() {
return new InsnListSectionIterator();
}
}