package net.fybertech.dynamicmappings;
import java.util.Iterator;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class MethodCallIterator implements Iterator<MethodCallIterator.MethodCall> {
private MethodNode method;
private AbstractInsnNode insn;
private MethodCallIterator.MethodCall next;
private boolean printWarnings;
private boolean printDebug;
private int index;
private Object[] stack;
private Object[] vars;
private int sp;
public MethodCallIterator(MethodNode method, boolean printWarnings, boolean printDebug) {
this.method = method;
this.insn = this.method.instructions.getFirst();
this.stack = new Object[100];
this.vars = new Object[100];
this.sp = 0;
this.index = 0;
this.printWarnings = printWarnings;
this.printDebug = printDebug;
this.next = null;
this.loadNext();
}
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public MethodCallIterator.MethodCall next() {
MethodCallIterator.MethodCall result = this.next;
this.loadNext();
return result;
}
private boolean loadNext() {
// Loop through the contents of registerBlocks and pull out any calls to registerBlock.
// Then map the block ids and class names based on the parameters to registerBlock from
// the mapping provided above.
boolean foundNext = false;
for (; insn != null && !foundNext; insn = insn.getNext())
{
if (insn instanceof LabelNode || insn instanceof LineNumberNode) {
// skip label/line numbers
index++;
continue;
}
if (insn instanceof InsnNode) {
// handle integer / float constants, just push to virtual stack
int opCode = ((InsnNode)insn).getOpcode();
switch (opCode) {
case Opcodes.ICONST_0:
stack[sp++] = 0;
break;
case Opcodes.ICONST_1:
stack[sp++] = 1;
break;
case Opcodes.ICONST_2:
stack[sp++] = 2;
break;
case Opcodes.ICONST_3:
stack[sp++] = 3;
break;
case Opcodes.ICONST_4:
stack[sp++] = 4;
break;
case Opcodes.ICONST_5:
stack[sp++] = 5;
break;
case Opcodes.DUP: // duplicate top of stack
stack[sp] = stack[sp - 1];
sp++;
break;
case Opcodes.FCONST_0:
stack[sp++] = 0f;
break;
case Opcodes.FCONST_1:
stack[sp++] = 1f;
break;
case Opcodes.FCONST_2:
stack[sp++] = 2f;
break;
default:
if (printWarnings) System.out.println("WARNING: discoverBlocks Unhandled InsnNode opcode: " + opCode);
break;
}
} else if (insn instanceof FieldInsnNode) {
// handle static field references, just push to virtual stack
FieldInsnNode fi = (FieldInsnNode)insn;
int opCode = fi.getOpcode();
switch (opCode) {
case Opcodes.GETSTATIC:
stack[sp++] = fi.name;
break;
default:
if (printWarnings) System.out.println("WARNING: discoverBlocks Unhandled FieldInsnNode opcode: " + opCode);
break;
}
} else if (insn instanceof TypeInsnNode) {
// handle type references, push new instance to virtual stack
TypeInsnNode ti = (TypeInsnNode)insn;
int opCode = ti.getOpcode();
switch (opCode) {
case Opcodes.NEW:
stack[sp++] = ti.desc;
break;
case Opcodes.ANEWARRAY:
stack[sp++] = ti.desc;
break;
default:
if (printWarnings) System.out.println("WARNING: discoverBlocks Unhandled TypeInsnNode opcode: " + opCode);
break;
}
} else if (insn instanceof MethodInsnNode) {
// handle method invocations, pop appropriate number of arguments off of virtual stack
// and check if method is registerBlock; if registerBlock is called then check class
// mapping and add if block id is found.
MethodInsnNode mi = (MethodInsnNode)insn;
int argCount = argCount(mi.desc);
if (insn.getOpcode() == Opcodes.INVOKESPECIAL) argCount++; // constructor consumes one item from the stack
if (printDebug) System.out.print(index + ": " + mi.name + " (");
Object[] tempArgs = new Object[argCount];
for (int i = argCount - 1; i >= 0; i--) {
tempArgs[i] = stack[--sp];
if (printDebug) System.out.print(stack[sp] + ",");
}
if (printDebug) System.out.println(")");
this.next = new MethodCallIterator.MethodCall(mi, tempArgs);
foundNext = true;
} else if (insn instanceof LdcInsnNode) {
// handle load constant value references, just push to virtual stack
LdcInsnNode li = (LdcInsnNode)insn;
stack[sp++] = li.cst;
} else if (insn instanceof VarInsnNode) {
// handle variable references, if it is a LOAD instruction then push to stack,
// if STORE instruction then pop from stack into virtual variable storage
VarInsnNode vi = (VarInsnNode)insn;
int opCode = vi.getOpcode();
switch (opCode) {
case Opcodes.ALOAD:
stack[sp++] = vars[vi.var];
//System.out.println("ALOAD: " + vars[vi.var]);
break;
case Opcodes.ASTORE:
vars[vi.var] = stack[--sp];
//System.out.println("var" + vi.var + " = " + stack[sp]);
break;
default:
if (printWarnings) System.out.println("WARNING: discoverBlocks Unhandled VarInsnNode opcode: " + opCode);
}
} else if (insn instanceof IntInsnNode) {
// handle integer types, just push value to virtual stack
IntInsnNode ii = (IntInsnNode)insn;
int opCode = ii.getOpcode();
switch (opCode) {
case Opcodes.BIPUSH:
stack[sp++] = ii.operand;
break;
case Opcodes.SIPUSH:
stack[sp++] = ii.operand;
break;
default:
if (printWarnings) System.out.println("WARNING: discoverBlocks Unhandled IntInsnNode opcode: " + opCode);
}
} else {
// unhandled type, display warning for troubleshooting.
if (printWarnings) System.out.println("WARNING: Unhandled IsnsNode " + index + ": " + insn.toString());
}
}
if (!foundNext) this.next = null;
return foundNext;
}
/**
* Counts the number of arguments that must be passed into a method
* based on the method signature provided.
* @param methodDesc
* @return
*/
public static int argCount(String methodDesc) {
int count = 0;
boolean complexType = false;
for (int i = 1; i < methodDesc.length() && methodDesc.charAt(i) != ')'; i++) {
if (complexType) {
if (methodDesc.charAt(i) == ';') {
complexType = false;
count++;
}
continue;
}
if (methodDesc.charAt(i) == '[' || methodDesc.charAt(i) == 'L')
complexType = true;
else
count++;
}
return count;
}
public class MethodCall {
public MethodInsnNode methodNode;
public Object[] args;
public MethodCall(MethodInsnNode methodNode, Object[] args) {
this.methodNode = methodNode;
this.args = args;
}
}
}