/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package EDU.purdue.cs.bloat.inline; import java.util.*; import EDU.purdue.cs.bloat.editor.*; import EDU.purdue.cs.bloat.util.*; /** * The of <tt>InstructionStack</tt> keeps track of which instructions pushed a * certain element of the stack. It is a stack of sets of instructions. You can * think of it like this: (1, {4,6}, 8) means that instruction 1 pushed the item * on the bottom of the stack, instructions 4 and 6 both push the second element * on the stack (depending on control flow), and instruction 8 pushed the * element on top of the stack. We use this information at a call site to * determine what instruction(s) pushed the receiver object onto the stack. * Special thanks to Jan Vitek for helping me come up with this algorithm. * * <p> * * This class is an <tt>InstructionVisitor</tt> that updates the instruction * stack representation appropriately. When there is a merge in control flow, * two <tt>InstructionStack</tt>s are merged using the <tt>merge</tt> * method. * * <p> * * This class is also used to determine whether the object at a given stack * depth is "preexistent". An object preexists if we can guarantee that it was * created outside of the method in which it is used. While it is possible to * determine which fields are preexistent (see "Inlining of Virtual Methods" by * Detlefs and Ageson in ECOOP99), we only keep track of local variables that * preexist. * * <p> * * We determine which local variables preexist as follows. Initially, only the * local variables for method parameters preexist. When a store is encoutered, * we determine if the set of instructions on top of the stack consist of loads * preexistent variables. If so, then the variable being stored into is * preexistent. However, objects that are the result of an allocation * (constructor call) in the method are preexist. Thus, we maintain the preexist * information as a set. If the set is null, then the object does not preexist. * If the set is empty, then it preexists and came from at least one argument. * If the set is non-empty, then it contains the type(s) of the constructor(s) * from which it originated. Pretty neat, huh? */ public class InstructionStack extends InstructionAdapter { MethodEditor method; // Method we're dealing with HashMap stacks; // Maps Labels to their stacks LinkedList currStack; // The current stack HashMap preexists; // Maps Labels to their preexists HashMap currPreexists; // The current preexist (var -> Set) private static void pre(final String s) { // Debug preexistence if (false) { System.out.println(s); } } /** * Constructor. Creates an empty <tt>InstructionStack</tt>. */ public InstructionStack(final MethodEditor method) { this.method = method; this.stacks = new HashMap(); this.preexists = new HashMap(); // Initially only the parameters to the method prexist final Type[] paramTypes = method.paramTypes(); this.currPreexists = new HashMap(); for (int i = 0; i < paramTypes.length; i++) { // We only care about the preexistence of objects (not arrays) if ((paramTypes[i] != null) && paramTypes[i].isObject()) { this.currPreexists.put(method.paramAt(i), new HashSet()); } } } /** * Deals with a Label. */ public void handle(final Label label) { final LinkedList stack = (LinkedList) stacks.get(label); if (stack == null) { // If this label starts an exception handler, account for the // exception being pushed on the stack final Iterator tryCatches = method.tryCatches().iterator(); while (tryCatches.hasNext()) { final TryCatch tc = (TryCatch) tryCatches.next(); if (tc.handler().equals(label)) { // Kluge to push the exception object on the stack. I don't // think it matters much. final Instruction aload = new Instruction(Opcode.opcx_ldc, "Exception"); currStack = new LinkedList(); aload.visit(this); stacks.put(label, stack); label.setStartsBlock(true); // We have no idea from where the exception will be thrown, // so we can't make any assumptions about the preexistence // of any variables. currPreexists = new HashMap(); return; } } if (currStack == null) { // Make a new stack currStack = new LinkedList(); stacks.put(label, currStack); // I don't think we need to worry about the currPreexists. It // was taken care of in the constructor. } else { // Otherwise, keep the current stack currStack = (LinkedList) currStack.clone(); stacks.put(label, currStack); // And the current preexists currPreexists = InstructionStack.clonePreexists(currPreexists); this.preexists.put(label, currPreexists); } } else { // Merge the old stack with the current one currStack = InstructionStack.merge(currStack, stack); stacks.put(label, currStack); final HashMap oldPreexists = (HashMap) this.preexists.get(label); currPreexists = InstructionStack.merge(oldPreexists, currPreexists); this.preexists.put(label, currPreexists); } } /** * Deals with an <tt>Instruction</tt> handles branches, jsrs, and the * like. */ public void handle(final Instruction inst) { // Visit first inst.visit(this); if (inst.isJump()) { final Label target = (Label) inst.operand(); target.setStartsBlock(true); // Merge the target's stack with any other stacks at that // label final LinkedList targetStack = (LinkedList) stacks.get(target); if (targetStack != null) { // Don't change currStack, but do the merge stacks.put(target, InstructionStack.merge(currStack, targetStack)); final HashMap oldPreexists = (HashMap) this.preexists .get(target); this.preexists.put(target, InstructionStack.merge( currPreexists, oldPreexists)); } else { // Put a new stack at the target stacks.put(target, currStack.clone()); this.preexists.put(target, InstructionStack .clonePreexists(currPreexists)); } if (!inst.isConditionalJump()) { // The next instruction should be a Label. But since it is // not the next instruction executed, we don't want to merge // the contents of the label's stack and the current stack. // So, null out the current stack. currStack = new LinkedList(); } } else if (inst.isSwitch()) { // Propagate the current stack to all targets final Switch sw = (Switch) inst.operand(); final Label defaultTarget = sw.defaultTarget(); defaultTarget.setStartsBlock(true); final LinkedList defaultStack = (LinkedList) stacks .get(defaultTarget); if (defaultStack != null) { Assert.isTrue(defaultStack.size() == currStack.size(), "Stack height mismatch (" + defaultStack.size() + " != " + currStack.size() + ") at " + inst); // Merge stacks for good measure stacks.put(defaultTarget, InstructionStack.merge(currStack, defaultStack)); final HashMap defaultPreexists = (HashMap) this.preexists .get(defaultTarget); this.preexists.put(defaultTarget, InstructionStack.merge( currPreexists, defaultPreexists)); } else { // Put copy of stack at target stacks.put(defaultTarget, currStack.clone()); this.preexists.put(defaultTarget, InstructionStack .clonePreexists(currPreexists)); } final Label[] targets = sw.targets(); for (int t = 0; t < targets.length; t++) { final Label target = targets[t]; target.setStartsBlock(true); final LinkedList targetStack = (LinkedList) stacks.get(target); if (targetStack != null) { Assert.isTrue(targetStack.size() == currStack.size(), "Stack height mismatch (" + targetStack.size() + " != " + currStack.size() + ") at " + inst); // Merge stacks for good measure stacks.put(target, InstructionStack.merge(currStack, targetStack)); final HashMap oldPreexists = (HashMap) this.preexists .get(target); this.preexists.put(target, InstructionStack.merge( oldPreexists, currPreexists)); } else { stacks.put(target, currStack.clone()); this.preexists.put(target, InstructionStack .clonePreexists(currPreexists)); } } } else if (inst.isJsr()) { // Someday we might have to deal with subroutines that push // stuff on the stack. That complicates things. I'm going to // pretend it doesn't exist. It was good enough for Nate. // In the meantime, we have to propagate the fact that the jsr // pushes the return address to the subroutine. We use an empty // stack because it is possible that a subroutine could be // called with different stack heights. Here is another thing // that needs to be fixed someday. final LinkedList subStack = new LinkedList(); final LinkedList oldStack = currStack; // Push the return address on stack currStack = subStack; inst.visit(this); currStack = oldStack; // Should be okay unless sub effects stack // Propagate subStack to subroutine final Label subroutine = (Label) inst.operand(); subroutine.setStartsBlock(true); stacks.put(subroutine, subStack); this.preexists.put(subroutine, new HashMap()); } else if (inst.isReturn() || inst.isThrow()) { // We don't what comes next, but we don't want to merge with the // current stack. currStack = new LinkedList(); } } /** * Pushes an instruction onto the stack */ private void push(final Instruction inst) { // Create a new set for the top element of the stack final Set set = new HashSet(); set.add(inst); currStack.add(set); } /** * Pops the top of the stack. */ private void pop() { currStack.removeLast(); } /** * Pops a given number of elements off the stack. */ private void pop(final int n) { for (int i = 0; i < n; i++) { currStack.removeLast(); } } /** * Returns the number of elements in this instruction stack. */ public int height() { return (currStack.size()); } /** * Returns the set of <tt>Instruction</tt>s at depth <tt>n</tt> of the * instruction stack. Depth 0 is the top of the stack. The bottom of the * stack is at depth stackSize - 1. */ public Set atDepth(final int n) { final Set set = (Set) currStack.get(currStack.size() - 1 - n); return (set); } /** * Returns a <tt>Set</tt> representing whether or not the instructions at * a given depth push a preexistent object onto the stack. If the list is * <tt>null</tt>, then the push is not preexistent. If the list is empty, * then the push is preexistent. If the list is non-empty, it contains the * <tt>Type</tt>(s) of objects that are known to be on the stack. These * types are the results of calls to constructors. */ public HashSet preexistsAtDepth(final int n) { // How do we determine whether a set of instructions pushes a // preexist object? All of the instructions must be loads of // objects from preexistent variable or the result of an // object creation. Note that we can deal with arrays because // we'd have to keep track of indices. InstructionStack.pre(" Preexisting variables: " + InstructionStack.db(currPreexists)); HashSet atDepth = null; final Iterator insts = this.atDepth(n).iterator(); Assert.isTrue(insts.hasNext(), "No instructions at depth " + n); while (insts.hasNext()) { final Instruction inst = (Instruction) insts.next(); InstructionStack.pre(" Instruction at " + n + ": " + inst); if (inst.opcodeClass() == Opcode.opcx_aload) { final LocalVariable var = (LocalVariable) inst.operand(); final Set set = (Set) this.currPreexists.get(var); if (set != null) { if (set.isEmpty()) { // If the set is empty, then this local variable came // from // a method argument. atDepth = new HashSet(); } else { // The list contains types that are the result of a // constructor call. Add them to the preexists list. if (atDepth == null) { atDepth = new HashSet(); } atDepth.addAll(set); } continue; } // Instruction loads a non-preexistent variable, fall through } else if (inst.opcodeClass() == Opcode.opcx_new) { // We look for the new instruction instead of the constructor // call because of the way we represent the stack. if ((atDepth != null) && atDepth.isEmpty()) { // We already know that the object pushed at this depth are // one of the arguments. We don't the exact type of the // argument, so we can't safely add the type being // instantiated to the preexist list. continue; } // Figure out the type being created and add it to the // preexists list. final Type type = (Type) inst.operand(); InstructionStack.pre(" Constructing " + Type.truncatedName(type)); if (atDepth == null) { atDepth = new HashSet(); } atDepth.add(type); continue; // A non-constructor invokespecial was called, fall through } else if (inst.opcodeClass() == Opcode.opcx_dup) { final Set set = this.preexistsAtDepth(n - 1); if (set != null) { if (set.isEmpty()) { // If list is empty, then this preexist must also be // empty atDepth = new HashSet(); } else { // Add the classes instantiated to the list atDepth.addAll(set); } continue; } } InstructionStack.pre(" Doesn't preexist"); return (null); } // If we got down here every instruction was preexistent InstructionStack.pre(" Preexists"); return (atDepth); } /** * Merges two stacks together and returns their union. Note that stacks of * unequal height cannot be merged. */ private static LinkedList merge(final LinkedList stack1, final LinkedList stack2) { Assert.isFalse((stack1 == null) && (stack2 == null), "Cannot merge two null stacks"); final LinkedList merge = new LinkedList(); // If either stack is null or empty, just use the other one if ((stack1 == null) || (stack1.size() == 0)) { merge.addAll(stack2); return (merge); } if ((stack2 == null) || (stack2.size() == 0)) { merge.addAll(stack1); return (merge); } Assert.isTrue(stack1.size() == stack2.size(), "Stacks of unequal height cannot be merged (" + stack1.size() + " != " + stack2.size() + ")"); for (int i = 0; i < stack1.size(); i++) { final Set mergeSet = new HashSet(); mergeSet.addAll((Set) stack1.get(i)); mergeSet.addAll((Set) stack2.get(i)); merge.add(i, mergeSet); } return (merge); } /** * Merges two preexists lists. For a given variable, if either of the two * input indices is <tt>null</tt>, then the result is <tt>null</tt>. * If either of the two input indices is empty, then the result is empty. * Otherwise, the result is the union of the two input sets. */ private static HashMap merge(final HashMap one, final HashMap two) { Assert.isFalse((one == null) && (two == null), "Can't merge null preexists"); if (one == null) { return (InstructionStack.clonePreexists(two)); } else if (two == null) { return (InstructionStack.clonePreexists(one)); } // Go through all of the variables in both sets. If one is not // contained in the other, then the set (or null) from the other // is used. If one is mapped to null, then the result is null. // If one has an empty set, then the result has an empty set. // Otherwise, the two non-empty sets are merge. final HashMap result = new HashMap(); final Set allVars = new HashSet(); allVars.addAll(one.keySet()); allVars.addAll(two.keySet()); final Iterator iter = allVars.iterator(); while (iter.hasNext()) { final LocalVariable var = (LocalVariable) iter.next(); if (!one.containsKey(var)) { HashSet set = (HashSet) two.get(var); if (set != null) { set = (HashSet) set.clone(); } result.put(var, set); } else if (!two.containsKey(var)) { HashSet set = (HashSet) one.get(var); if (set != null) { set = (HashSet) set.clone(); } result.put(var, set); } else { final HashSet oneSet = (HashSet) one.get(var); final HashSet twoSet = (HashSet) two.get(var); if ((oneSet == null) || (twoSet == null)) { result.put(var, null); } else if (oneSet.isEmpty() || twoSet.isEmpty()) { result.put(var, new HashSet()); } else { final Set set = new HashSet(); set.addAll(oneSet); set.addAll(twoSet); result.put(var, set); } } } InstructionStack.pre("Merge of " + InstructionStack.db(one) + " and " + InstructionStack.db(two) + " is " + InstructionStack.db(result)); return (result); } /** * Returns a textual representation of a preexists mapping. */ static String db(final HashMap preexists) { if (preexists == null) { return ("\n null?\n"); } final StringBuffer sb = new StringBuffer("\n"); final Iterator vars = preexists.keySet().iterator(); while (vars.hasNext()) { final LocalVariable var = (LocalVariable) vars.next(); final Set set = (Set) preexists.get(var); if (set == null) { sb.append(" " + var + ": null\n"); } else { sb.append(" " + var + ": "); final Iterator iter = set.iterator(); while (iter.hasNext()) { final Type type = (Type) iter.next(); sb.append(Type.truncatedName(type)); if (iter.hasNext()) { sb.append(", "); } } sb.append("\n"); } } return (sb.toString()); } /** * Makes a deep copy of a List containing preexists information. */ private static HashMap clonePreexists(final HashMap old) { final HashMap clone = new HashMap(); final Iterator vars = old.keySet().iterator(); while (vars.hasNext()) { final LocalVariable var = (LocalVariable) vars.next(); final HashSet set = (HashSet) old.get(var); if (set == null) { clone.put(var, null); } else { clone.put(var, set.clone()); } } return (clone); } public void visit_nop(final Instruction inst) { } public void visit_ldc(final Instruction inst) { push(inst); } public void visit_iload(final Instruction inst) { push(inst); } public void visit_lload(final Instruction inst) { push(inst); } public void visit_fload(final Instruction inst) { push(inst); } public void visit_dload(final Instruction inst) { push(inst); } public void visit_aload(final Instruction inst) { push(inst); } public void visit_iaload(final Instruction inst) { pop(2); push(inst); } public void visit_laload(final Instruction inst) { pop(2); push(inst); } public void visit_faload(final Instruction inst) { pop(2); push(inst); } public void visit_daload(final Instruction inst) { pop(2); push(inst); } public void visit_aaload(final Instruction inst) { pop(2); push(inst); } public void visit_baload(final Instruction inst) { pop(2); push(inst); } public void visit_caload(final Instruction inst) { pop(2); push(inst); } public void visit_saload(final Instruction inst) { pop(2); push(inst); } public void visit_istore(final Instruction inst) { pop(); } public void visit_lstore(final Instruction inst) { pop(); } public void visit_fstore(final Instruction inst) { pop(); } public void visit_dstore(final Instruction inst) { pop(); } public void visit_astore(final Instruction inst) { // When we store an object to a local variable, we need to keep // track of whether or not the object being stored (and hence the // variable into which it is stored) is preexistent. final LocalVariable var = (LocalVariable) inst.operand(); final HashSet set = preexistsAtDepth(0); if (set == null) { InstructionStack.pre(" " + var + " does not preexist"); this.currPreexists.put(var, null); } else if (set.isEmpty()) { InstructionStack.pre(" " + var + " preexists"); this.currPreexists.put(var, new HashSet()); } else { // This store superceeds anything else that was already in this // local, so don't merge the sets. InstructionStack.pre(" " + var + " preexists with types"); this.currPreexists.put(var, set.clone()); } pop(); } public void visit_iastore(final Instruction inst) { pop(3); } public void visit_lastore(final Instruction inst) { pop(3); } public void visit_fastore(final Instruction inst) { pop(3); } public void visit_dastore(final Instruction inst) { pop(3); } public void visit_aastore(final Instruction inst) { pop(3); } public void visit_bastore(final Instruction inst) { pop(3); } public void visit_castore(final Instruction inst) { pop(3); } public void visit_sastore(final Instruction inst) { pop(3); } /** * Helper method for asserting that all of the instructions are of a certain * category. */ private void checkCategory(final Set insts, final int category) { final Iterator iter = insts.iterator(); while (iter.hasNext()) { final Instruction inst = (Instruction) iter.next(); Assert.isTrue(inst.category() == category, "Category mismatch: " + inst.category() + " != " + category); } } /** * Helper method for asserting that all of the instructions have the same * category. The category is returned. */ private int checkCategory(final Set insts) { int category = 0; final Iterator iter = insts.iterator(); while (iter.hasNext()) { final Instruction inst = (Instruction) iter.next(); if (category == 0) { category = inst.category(); } else { Assert.isTrue(inst.category() == category, "Category mismatch in instruction set"); } } Assert.isTrue(category != 0, "No instructions in set"); return (category); } public void visit_pop(final Instruction inst) { final Set insts = atDepth(0); checkCategory(insts, 1); pop(); } public void visit_pop2(final Instruction inst) { // Form 1 pops two category 1 values off the stack. Form 2 pops // one category 2 value off the stack. final Set top1 = (Set) currStack.removeLast(); final int category = checkCategory(top1); if (category == 1) { // Pop another category 1 off final Set top2 = (Set) currStack.removeLast(); checkCategory(top2, 1); } } public void visit_dup(final Instruction inst) { // Duplicate the category 1 value on the top of the stack final Set dup = atDepth(0); checkCategory(dup, 1); currStack.add(new HashSet(dup)); } public void visit_dup_x1(final Instruction inst) { final Set dup = atDepth(0); checkCategory(dup, 1); currStack.add(currStack.size() - 2, new HashSet(dup)); } public void visit_dup_x2(final Instruction inst) { // Top value on stack must be category 1. final Set top1 = atDepth(0); checkCategory(top1, 1); final Set top2 = atDepth(1); final int category = checkCategory(top2); if (category == 1) { final Set top3 = atDepth(2); checkCategory(top3, 1); // Form 1: Dup top value and put three down currStack.add(currStack.size() - 3, new HashSet(top1)); } else { // Form 2: Dup top value and put two down currStack.add(currStack.size() - 2, new HashSet(top1)); } } public void visit_dup2(final Instruction inst) { // If top two values are both category 1, dup them both. // Otherwise, dup the one category 2 value. final Set top = atDepth(0); final int category = checkCategory(top); if (category == 1) { final Set top1 = atDepth(1); checkCategory(top1, 1); // Form 1: Dup top two values currStack.add(new HashSet(top1)); currStack.add(new HashSet(top)); } else { // Form 2: Dup top value currStack.add(new HashSet(top)); } } public void visit_dup2_x1(final Instruction inst) { // If the top two values are of category 1, then dup them and put // them three down. Otherwise, the top two values are of category // 2 and the top value is put two down. final Set top = atDepth(0); final int category = checkCategory(top); if (category == 1) { final Set top1 = atDepth(1); checkCategory(top1, 1); // Form 1: Dup top two values and put three down final int n = currStack.size() - 3; currStack.add(n, top1); currStack.add(n, top); } else { final Set top1 = atDepth(1); checkCategory(top1, 1); // Form 2: Dup top value and put two down currStack.add(currStack.size() - 2, new HashSet(top)); } } public void visit_dup2_x2(final Instruction inst) { // If the two four values are all category 1, then duplicate the // top two values and put them four down. If the top value is of // category 2 and the next two are of type 1, then dup the top // value and put it three down. If the top two values are both // category 1 and the third value is type 2, then dup the top two // values and put them three down. If the top two values are both // category 2, then dup the top one and put it two down. final Set top = atDepth(0); final int category = checkCategory(top); if (category == 1) { final Set top1 = atDepth(1); final int category1 = checkCategory(top1); if (category1 == 1) { final Set top2 = atDepth(2); final int category2 = checkCategory(top2); if (category2 == 1) { checkCategory(atDepth(3), 1); // Form 1: Dup top two values and put four down final int n = currStack.size() - 4; currStack.add(n, new HashSet(top1)); currStack.add(n, new HashSet(top)); } else { // Form 3: Dup top two values and put three down final int n = currStack.size() - 3; currStack.add(n, new HashSet(top1)); currStack.add(n, new HashSet(top)); } } else { Assert.isTrue(false, "Impossible stack combination for " + "dup2_x1: ... 2 1"); } } else { final Set top1 = atDepth(1); final int category1 = checkCategory(top1); if (category1 == 1) { final int category2 = checkCategory(atDepth(2)); if (category2 == 1) { // Form 2: Dup top value and put three down currStack.add(currStack.size() - 3, new HashSet(top)); } else { Assert.isTrue(false, "Impossible stack combination for " + "dup2_x1: ... 2 1 2"); } } else { // Form 4: Dup top and put two down currStack.add(currStack.size() - 2, new HashSet(top)); } } } public void visit_swap(final Instruction inst) { final Set top = (Set) currStack.removeLast(); final Set next = (Set) currStack.removeLast(); checkCategory(top, 1); checkCategory(next, 1); currStack.add(top); currStack.add(next); } public void visit_iadd(final Instruction inst) { pop(2); push(inst); } public void visit_ladd(final Instruction inst) { pop(2); push(inst); } public void visit_fadd(final Instruction inst) { pop(2); push(inst); } public void visit_dadd(final Instruction inst) { pop(2); push(inst); } public void visit_isub(final Instruction inst) { pop(2); push(inst); } public void visit_lsub(final Instruction inst) { pop(2); push(inst); } public void visit_fsub(final Instruction inst) { pop(2); push(inst); } public void visit_dsub(final Instruction inst) { pop(2); push(inst); } public void visit_imul(final Instruction inst) { pop(2); push(inst); } public void visit_lmul(final Instruction inst) { pop(2); push(inst); } public void visit_fmul(final Instruction inst) { pop(2); push(inst); } public void visit_dmul(final Instruction inst) { pop(2); push(inst); } public void visit_idiv(final Instruction inst) { pop(2); push(inst); } public void visit_ldiv(final Instruction inst) { pop(2); push(inst); } public void visit_fdiv(final Instruction inst) { pop(2); push(inst); } public void visit_ddiv(final Instruction inst) { pop(2); push(inst); } public void visit_irem(final Instruction inst) { pop(2); push(inst); } public void visit_lrem(final Instruction inst) { pop(2); push(inst); } public void visit_frem(final Instruction inst) { pop(2); push(inst); } public void visit_drem(final Instruction inst) { pop(2); push(inst); } public void visit_ineg(final Instruction inst) { pop(); push(inst); } public void visit_lneg(final Instruction inst) { pop(); push(inst); } public void visit_fneg(final Instruction inst) { pop(); push(inst); } public void visit_dneg(final Instruction inst) { pop(); push(inst); } public void visit_ishl(final Instruction inst) { pop(2); push(inst); } public void visit_lshl(final Instruction inst) { pop(2); push(inst); } public void visit_ishr(final Instruction inst) { pop(2); push(inst); } public void visit_lshr(final Instruction inst) { pop(2); push(inst); } public void visit_iushr(final Instruction inst) { pop(2); push(inst); } public void visit_lushr(final Instruction inst) { pop(2); push(inst); } public void visit_iand(final Instruction inst) { pop(2); push(inst); } public void visit_land(final Instruction inst) { pop(2); push(inst); } public void visit_ior(final Instruction inst) { pop(2); push(inst); } public void visit_lor(final Instruction inst) { pop(2); push(inst); } public void visit_ixor(final Instruction inst) { pop(2); push(inst); } public void visit_lxor(final Instruction inst) { pop(2); push(inst); } public void visit_iinc(final Instruction inst) { // Kind of a fine point here. The instruction doesn't change the // stack, iinc increments a local variable. } public void visit_i2l(final Instruction inst) { pop(); push(inst); } public void visit_i2f(final Instruction inst) { pop(); push(inst); } public void visit_i2d(final Instruction inst) { pop(); push(inst); } public void visit_l2i(final Instruction inst) { pop(); push(inst); } public void visit_l2f(final Instruction inst) { pop(); push(inst); } public void visit_l2d(final Instruction inst) { pop(); push(inst); } public void visit_f2i(final Instruction inst) { pop(); push(inst); } public void visit_f2l(final Instruction inst) { pop(); push(inst); } public void visit_f2d(final Instruction inst) { pop(); push(inst); } public void visit_d2i(final Instruction inst) { pop(); push(inst); } public void visit_d2l(final Instruction inst) { pop(); push(inst); } public void visit_d2f(final Instruction inst) { pop(); push(inst); } public void visit_i2b(final Instruction inst) { pop(); push(inst); } public void visit_i2c(final Instruction inst) { pop(); push(inst); } public void visit_i2s(final Instruction inst) { pop(); push(inst); } public void visit_lcmp(final Instruction inst) { pop(2); push(inst); } public void visit_fcmpl(final Instruction inst) { pop(2); push(inst); } public void visit_fcmpg(final Instruction inst) { pop(2); push(inst); } public void visit_dcmpl(final Instruction inst) { pop(2); push(inst); } public void visit_dcmpg(final Instruction inst) { pop(2); push(inst); } public void visit_ifeq(final Instruction inst) { pop(); } public void visit_ifne(final Instruction inst) { pop(); } public void visit_iflt(final Instruction inst) { pop(); } public void visit_ifge(final Instruction inst) { pop(); } public void visit_ifgt(final Instruction inst) { pop(); } public void visit_ifle(final Instruction inst) { pop(); } public void visit_if_icmpeq(final Instruction inst) { pop(2); } public void visit_if_icmpne(final Instruction inst) { pop(2); } public void visit_if_icmplt(final Instruction inst) { pop(2); } public void visit_if_icmpge(final Instruction inst) { pop(2); } public void visit_if_icmpgt(final Instruction inst) { pop(2); } public void visit_if_icmple(final Instruction inst) { pop(2); } public void visit_if_acmpeq(final Instruction inst) { pop(2); } public void visit_if_acmpne(final Instruction inst) { pop(2); } public void visit_goto(final Instruction inst) { // Nothing to do } public void visit_jsr(final Instruction inst) { push(inst); } public void visit_ret(final Instruction inst) { // Nothing to do } public void visit_switch(final Instruction inst) { pop(); } // Return stuff performed by handle(Instruction) public void visit_ireturn(final Instruction inst) { } public void visit_lreturn(final Instruction inst) { } public void visit_freturn(final Instruction inst) { } public void visit_dreturn(final Instruction inst) { } public void visit_areturn(final Instruction inst) { } public void visit_return(final Instruction inst) { } public void visit_getstatic(final Instruction inst) { push(inst); } public void visit_putstatic(final Instruction inst) { pop(); } public void visit_putstatic_nowb(final Instruction inst) { pop(); } public void visit_getfield(final Instruction inst) { pop(); push(inst); } public void visit_putfield(final Instruction inst) { pop(2); } public void visit_putfield_nowb(final Instruction inst) { pop(2); } public void visit_invokevirtual(final Instruction inst) { final MemberRef method = (MemberRef) inst.operand(); final Type type = method.nameAndType().type(); pop(type.paramTypes().length); pop(); // Pop receiver if (type.returnType() != Type.VOID) { push(inst); } } public void visit_invokespecial(final Instruction inst) { final MemberRef method = (MemberRef) inst.operand(); final Type type = method.nameAndType().type(); pop(type.paramTypes().length); pop(); // Pop receiver if (type.returnType() != Type.VOID) { push(inst); } } public void visit_invokestatic(final Instruction inst) { final MemberRef method = (MemberRef) inst.operand(); final Type type = method.nameAndType().type(); pop(type.paramTypes().length); if (type.returnType() != Type.VOID) { push(inst); } } public void visit_invokeinterface(final Instruction inst) { final MemberRef method = (MemberRef) inst.operand(); final Type type = method.nameAndType().type(); pop(type.paramTypes().length); pop(); // Pop receiver if (type.returnType() != Type.VOID) { push(inst); } } public void visit_new(final Instruction inst) { push(inst); } public void visit_newarray(final Instruction inst) { pop(); push(inst); } public void visit_arraylength(final Instruction inst) { pop(); push(inst); } public void visit_athrow(final Instruction inst) { // I guess... pop(); push(inst); } public void visit_checkcast(final Instruction inst) { pop(); push(inst); } public void visit_instanceof(final Instruction inst) { pop(); push(inst); } public void visit_monitorenter(final Instruction inst) { pop(); } public void visit_monitorexit(final Instruction inst) { pop(); } public void visit_multianewarray(final Instruction inst) { final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); final int dim = operand.dimensions(); pop(dim); push(inst); } public void visit_ifnull(final Instruction inst) { pop(); } public void visit_ifnonnull(final Instruction inst) { pop(); } public void visit_rc(final Instruction inst) { } public void visit_aupdate(final Instruction inst) { } public void visit_supdate(final Instruction inst) { } public void visit_aswizzle(final Instruction inst) { } public void visit_aswrange(final Instruction inst) { } }