package org.objectweb.asm.commons.cfg.tree.node;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.cfg.tree.NodeTree;
import org.objectweb.asm.commons.cfg.tree.NodeVisitor;
import org.objectweb.asm.commons.cfg.tree.Tree;
import org.objectweb.asm.commons.util.Assembly;
import org.objectweb.asm.tree.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public class AbstractNode extends Tree<AbstractNode> implements Opcodes {
public static final String CHILD = ">";
public static final String NUMBER = "#";
public static final String[] QUERIES = {NUMBER};
public static final String ARITHMETIC_NODE = "ARITHMETIC";
public static final String[] NODE_QUERIES = {ARITHMETIC_NODE};
public static final Class<?> ARITHMETIC_NODE_CLASS = ArithmeticNode.class;
public static final Class<?>[] NODE_QUERY_CLASSES = {ARITHMETIC_NODE_CLASS};
public int collapsed, producing;
private NodeTree tree;
private AbstractInsnNode insn;
private int produceCount;
private boolean handler;
private AbstractInsnNode[] instructions;
public AbstractNode(NodeTree tree, AbstractInsnNode insn, int collapsed, int producing) {
this.tree = tree;
this.insn = insn;
this.collapsed = collapsed;
this.producing = (produceCount = producing);
}
public ClassNode caller() {
return tree.method().owner;
}
public void accept(NodeVisitor nv) {
nv.visitAny(this);
switch (insn.type()) {
case INSN: {
if (opcode() >= ICONST_M1 && opcode() <= DCONST_1) {
nv.visitNumber((NumberNode) this);
} else if (opcode() >= I2L && opcode() <= I2S) {
nv.visitConversion((ConversionNode) this);
} else if (opcode() >= IADD && opcode() <= LXOR) {
nv.visitOperation((ArithmeticNode) this);
} else {
nv.visit(this);
}
break;
}
case INT_INSN: {
nv.visitNumber((NumberNode) this);
break;
}
case VAR_INSN: {
nv.visitVariable((VariableNode) this);
break;
}
case TYPE_INSN: {
nv.visitType((TypeNode) this);
break;
}
case FIELD_INSN: {
nv.visitField((FieldMemberNode) this);
break;
}
case METHOD_INSN: {
nv.visitMethod((MethodMemberNode) this);
break;
}
case JUMP_INSN: {
nv.visitJump((JumpNode) this);
break;
}
case LABEL: {
nv.visitLabel(this);
break;
}
case LDC_INSN: {
Object cst = ((LdcInsnNode) insn()).cst;
if (cst != null && cst instanceof Number) {
nv.visitNumber((NumberNode) this);
} else {
nv.visitConstant((ConstantNode) this);
}
break;
}
case IINC_INSN: {
nv.visitIinc((IincNode) this);
break;
}
case TABLESWITCH_INSN: {
nv.visitTableSwitch(this);
break;
}
case LOOKUPSWITCH_INSN: {
nv.visitLookupSwitch(this);
break;
}
case MULTIANEWARRAY_INSN: {
nv.visitMultiANewArray(this);
break;
}
case FRAME: {
nv.visitFrame(this);
break;
}
case LINE: {
nv.visitLine(this);
break;
}
}
}
public AbstractInsnNode[] collapse() {
if (instructions != null) {
return instructions;
}
instructions = new AbstractInsnNode[total()];
int i = 0;
for (AbstractNode n : this) {
AbstractInsnNode[] nodes = n.collapse();
System.arraycopy(nodes, 0, instructions, i, nodes.length);
i += nodes.length;
}
if (instructions.length - i != 1) {
throw new RuntimeException();
}
instructions[i] = insn();
return instructions;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AbstractNode && Assembly.instructionsEqual(((AbstractNode) obj).insn(), insn());
}
public AbstractInsnNode insn() {
return insn;
}
public boolean isHandler() {
return handler;
}
public MethodNode method() {
return tree.method();
}
public int opcode() {
return insn != null ? insn.opcode() : -1;
}
public void pop() {
parent().remove(this);
}
public boolean hasFirst() {
return first() != null;
}
public AbstractNode first() {
return child(0);
}
public AbstractNode child(int idx) {
int i = 0;
for (AbstractNode n : this) {
if (i == idx) return n;
i++;
}
return null;
}
public AbstractNode[] producing() {
AbstractNode[] nodes = new AbstractNode[size()];
int i = 0;
for (AbstractNode n : this) {
if (n.produceCount > 0) {
nodes[i++] = n;
}
}
return Arrays.copyOf(nodes, i);
}
public void delete() {
parent().remove(this);
}
public void setHandler(boolean handler) {
this.handler = handler;
}
public void setInstruction(AbstractInsnNode insn) {
this.insn = insn;
}
@Override
public String toString() {
return toString(1);
}
protected String toString(int tab) {
StringBuilder sb = new StringBuilder();
sb.append(Assembly.toString(insn));
for (AbstractNode n : this) {
sb.append('\n');
for (int i = 0; i < tab; i++) {
sb.append('\t');
}
sb.append(n.toString(tab + 1));
}
return sb.toString();
}
public int total() {
int size = 1;
for (AbstractNode n : this) {
size += n.total();
}
return size;
}
public int children() {
return producing().length;
}
public NodeTree tree() {
return tree;
}
public int index() {
return method().instructions.indexOf(insn());//insn.insnIndex;
}
public AbstractNode first(int opcode) {
for (AbstractNode n : this) {
if (n.opcode() == opcode) return n;
}
return null;
}
public AbstractNode find(int opcode, int index) {
int i = 0;
for (AbstractNode n : this) {
if (n.opcode() == opcode) {
if (i++ == index) return n;
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T extends AbstractNode> T first(Class<? extends AbstractNode> clazz) {
for (AbstractNode n : this) {
if (n.getClass().equals(clazz)) return (T) n;
}
return null;
}
public NumberNode firstNumber() {
return first(NumberNode.class);
}
public ArithmeticNode firstOperation() {
return first(ArithmeticNode.class);
}
public ReferenceNode firstReference() {
return first(ReferenceNode.class);
}
public FieldMemberNode firstField() {
for (AbstractNode n : this) {
if (n instanceof ReferenceNode) {
if (n.insn() instanceof FieldInsnNode) return (FieldMemberNode) n;
}
}
return null;
}
public MethodMemberNode firstMethod() {
for (AbstractNode n : this) {
if (n instanceof ReferenceNode) {
if (n.insn() instanceof MethodInsnNode) return (MethodMemberNode) n;
}
}
return null;
}
public VariableNode firstVariable() {
return first(VariableNode.class);
}
public ConstantNode firstConstant() {
return first(ConstantNode.class);
}
public TypeNode firstType() {
return first(TypeNode.class);
}
public JumpNode firstJump() {
return first(JumpNode.class);
}
@SuppressWarnings("unchecked")
public <T extends AbstractNode> T next(Class<? extends AbstractNode> clazz, int max) {
int i = 0;
AbstractNode next = this;
while ((next = next.next()) != null && i++ < max) {
if (next.getClass().equals(clazz)) return (T) next;
}
return null;
}
public NumberNode nextNumber() {
return next(NumberNode.class, 3);
}
public ArithmeticNode nextOperation() {
return next(ArithmeticNode.class, 3);
}
public FieldMemberNode nextField(int max) {
return next(FieldMemberNode.class, max);
}
public FieldMemberNode nextField() {
return nextField(1);
}
public MethodMemberNode nextMethod(int max) {
return next(MethodMemberNode.class, max);
}
public ReferenceNode nextMethod() {
return nextMethod(1);
}
public JumpNode nextJump(int max) {
return next(JumpNode.class, max);
}
public JumpNode nextJump() {
return nextJump(1);
}
public VariableNode nextVariable(int max) {
return next(VariableNode.class, max);
}
public VariableNode nextVariable() {
return nextVariable(1);
}
public ConstantNode nextConstant(int max) {
return next(ConstantNode.class, max);
}
public ConstantNode nextConstant() {
return nextConstant(1);
}
public TypeNode nextType(int max) {
return next(TypeNode.class, max);
}
public TypeNode nextType() {
return nextType(1);
}
public AbstractNode next(int opcode, int max) {
int i = 0;
AbstractNode next = this;
while ((next = next.next()) != null && i++ < max) {
if (next.opcode() == opcode) return next;
}
return null;
}
public AbstractNode next(int opcode) {
return next(opcode, 5);
}
public AbstractNode previous(int opcode, int max) {
int i = 0;
AbstractNode prev = this;
while ((prev = prev.previous()) != null && i++ < max) {
if (prev.opcode() == opcode) return prev;
}
return null;
}
public AbstractNode previous(int opcode) {
return previous(opcode, 5);
}
public List<AbstractNode> findChildren(int opcode) {
List<AbstractNode> children = new ArrayList<>();
for (AbstractNode n : this) {
if (n.opcode() == opcode) children.add(n);
}
return !children.isEmpty() ? children : null;
}
public List<AbstractNode> layerAll(int... opcodes) {
List<AbstractNode> children = findChildren(opcodes[0]);
if (children == null) return null;
if (opcodes.length == 1) return children;
for (int i = 1; i < opcodes.length; i++) {
List<AbstractNode> next = new ArrayList<>();
for (AbstractNode n : children) {
List<AbstractNode> match = n.findChildren(opcodes[i]);
if (match == null) continue;
next.addAll(match);
}
if (next.isEmpty()) {
return null;
} else {
children.clear();
children.addAll(next);
}
}
return children;
}
public AbstractNode layer(int... opcodes) {
List<AbstractNode> nodes = layerAll(opcodes);
return nodes != null ? nodes.get(0) : null;
}
public AbstractNode preLayer(int... opcodes) {
AbstractNode node = this;
for (int opcode : opcodes) {
node = node.parent();
if (node == null || node.opcode() != opcode) return null;
}
return node;
}
public boolean hasChild(int opcode) {
return first(opcode) != null;
}
public String opname() {
try {
return Assembly.OPCODES[opcode()];
} catch (IndexOutOfBoundsException e) {
try {
return insn().getClass().getSimpleName();
} catch (Exception err) {
return insn().toString();
}
}
}
}