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();
}
}