/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2006, Rasmus Ulslev Pedersen This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 com.jopdesign.build; import java.util.*; import java.io.PrintWriter; import org.apache.bcel.classfile.*; import org.apache.bcel.Constants; import org.apache.bcel.generic.*; import org.apache.bcel.verifier.exc.*; import org.apache.bcel.verifier.structurals.*; /** * It is a class that are invoked two times for every non-abstract method. The * <code>stackWalker</code> method simulates the instructions of a given * method. Then this simulated context is used to extract information about the * incoming (ie. how the locals and the operands) frame looked for this * particular value of the program counter (PC). Then the information must reach * JOP. It is done by saving the garbage collection (GC) information below the * code address. We save one word that is packed with information about max. * number of local, max. number of operands etc. Then, if the method has ANY * reference for ANY value of the PC, the GC information is saved below this * info word. That is the case in the majority of the cases. As we need the type * (primitive or reference) for every local and every operand for every value of * the PC it can take up a lot of space. Initial experiments demonstrated an * overhead of 133% but now the default is an indexed approach. In this way the * X local bits and the Y operand bits are appended to form a pattern. These * patterns form a small number of unique patterns. Each PC is then mapped * (using just enough bits) to point to the corresponding unique pattern. The * unique patterns are packed at the end of bit map. When reading the file, we * refer to the "raw" and "indexed" approach. The raw aproach was saving the * aforementioned patterns for every PC. On JOP, a class called GCStkWalk is * responsible for identifying the references that may be among the locals and * the operands for the threads. A method named <code>swk</code> is * responsible for this scan. It walks the frames from the top and uses the * packes GC bit maps to determine which (if any) of the locals and operands * that hold references. It then returns a reference to an integer array that is * used in <code>GC.java</code> to push only the references onto into the * scanned list. Little note: The bits are written out from left to right in the * comments. The sum of the locals+operands cannot exceed 32. TODO: Test a * Gosling violation. TODO: Implement bytecode rearrangement when Gosling * violation detected. * * @author rup, ms */ public class GCRTMethodInfo { static int WORDLEN = 32; static int totalpcwords = 0; static int totalcntMgciWords = 0; static HashMap miMap = new HashMap(); // those methods with gc info // (provided by a call to CallGraph?) static HashSet gcMethods = null; /** * Contains the set of methods which are included in the stack map generation. * @param gcMethods */ public static void setGcMethods(HashSet gcMethods) { GCRTMethodInfo.gcMethods = gcMethods; } /** * Called from JOPizer->SetGCRTMethodInfo to run the stack simulation for * the method. * * @param mi the method */ public static void stackWalker(OldMethodInfo mi) { ((GCRTMethodInfo) miMap.get(mi)).stackWalker(); } /** * It runs the dump method without dumping. * * @param methodbcel * @return Returns the length in words that the GC bit maps will take. */ public static int gcLength(OldMethodInfo mi) { return ((GCRTMethodInfo) miMap.get(mi)).gcLength(); } /** * It writes out the gcpack and the bitmaps. Nice comments are dumped as * well in the .jop file. * * @param out * null if just the length is needed * @param mi * the method */ public static void dumpMethodGcis(OldMethodInfo mi, PrintWriter out) { ((GCRTMethodInfo) miMap.get(mi)).dumpMethodGcis(out); } public static void removePC(int pc,OldMethodInfo mi){ ((GCRTMethodInfo) miMap.get(mi)).removePC(pc); } // instance OldMethodInfo mi; Method method; int length; // Operand map for a given PC int[] ogci; // Local variables map for a given PC int[] mgci; // Instruction count int instCnt; // instCnt String[] pcinfo; int mstack, margs, mreallocals, len; // word counters for indexed approach int pcwords = 0; String tostr; String signature; String name; String cname; /** * Instanciated from from <code>SetClassInfo</code>. */ public GCRTMethodInfo(OldMethodInfo mi, Method method) { this.mi = mi; this.method = method; mgci = new int[0]; ogci = new int[0]; instCnt = 0; length = 0; if (miMap.containsKey(mi)) { System.err.println("Alredy added mi."); System.exit(-1); } else { miMap.put(mi, this); } } /** * It walks the stack frame at compile time. The locals and the operands are * mapped to to bit maps. These bit maps are used at run time to identify an * exact root set which are used in conjunction with a real-time garbage * collector scheduler. */ private void stackWalker() { // System.out.println("....................."); // System.out.println("stackWalker"); // System.out.println("Class // method:"+mi.cli.clazz.getClassName()+"."+mi.methodId); margs = mstack = mreallocals = len = instCnt = 0; Type at[] = method.getArgumentTypes(); for (int i = 0; i < at.length; ++i) { margs += at[i].getSize(); } if (!method.isStatic()) { margs++; // this } if (!method.isAbstract()) { mstack = method.getCode().getMaxStack(); mreallocals = method.getCode().getMaxLocals() - margs; if ((mreallocals+margs+mstack)>31) { // we interprete clinit on JOP - no size restriction if (!method.getName().equals("<clinit>")) { System.err.println("wrong size: "+method.getName()+" cannot have (mreallocals+margs+mstack)>31"); System.exit(-1); } } instCnt = (method.getCode().getCode()).length; operandWalker(); } // System.out.println(" *** // "+mi.cli.clazz.getClassName()+"."+mi.methodId+" --> mreallocals // ="+mreallocals+" margs ="+margs+" mstack ="+mstack); } /** * Collect type information on the operands for the stack frame. The code is * and then the execution context of each instructions is used to map the * type info of each entry on the stack. We map a reference entry with a 1 * and a non-reference entry with a 0. * * The simulation part is based of the BCEL Pass3bVerifer. */ private void operandWalker() { // System.out.println("operandWalker"); JavaClass jc = mi.getCli().clazz; ConstantPoolGen cpg = new ConstantPoolGen(jc.getConstantPool()); // Some methods overridden (see bottom of this file) InstConstraintVisitor icv = new AnInstConstraintVisitor(); icv.setConstantPoolGen(cpg); ExecutionVisitor ev = new ExecutionVisitor(); ev.setConstantPoolGen(cpg); MethodGen mg = new MethodGen(method, jc.getClassName(), cpg); // To later get the PC positions of the instructions mg.getInstructionList().setPositions(true); tostr = mg.toString(); signature = mg.getSignature(); name = mg.getName(); cname = mg.getClassName(); if(method.getName().equalsIgnoreCase("trace")){ boolean stop =true; } icv.setMethodGen(mg); if (!(mg.isAbstract() || mg.isNative())) { // IF mg HAS CODE (See pass // 2) ControlFlowGraph cfg = new ControlFlowGraph(mg); // InstructionContext as key for inFrame HashMap inFrames = new HashMap(); HashMap outFrames = new HashMap(); // // ArrayList array (size is length of bytecode) // // holds ArrayList objects with clones of OperandStack objects for // // each PC // ArrayList osa[] = new ArrayList[method.getCode().getCode().length]; // +1 // // because // // incoming // // frame // // to // // method // // is // // included // // ArrayList array (size is length of bytecode) // // holds ArrayList objects with clones of LocalVariables objects for // // each PC // ArrayList lva[] = new ArrayList[method.getCode().getCode().length]; // for (int i = 0; i < lva.length; i++) { // lva[i] = new ArrayList(); // osa[i] = new ArrayList(); // } // Build the initial frame situation for this method. FrameFrame fStart = new FrameFrame(mg.getMaxLocals(), mg .getMaxStack()); if (!mg.isStatic()) { if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)) { fStart.setThis(new UninitializedObjectType(new ObjectType( jc.getClassName()))); fStart.getLocals().set(0, fStart.getThis()); } else { fStart.setThis(null); fStart.getLocals() .set(0, new ObjectType(jc.getClassName())); } } Type[] argtypes = mg.getArgumentTypes(); int twoslotoffset = 0; for (int j = 0; j < argtypes.length; j++) { if (argtypes[j] == Type.SHORT || argtypes[j] == Type.BYTE || argtypes[j] == Type.CHAR || argtypes[j] == Type.BOOLEAN) { argtypes[j] = Type.INT; } fStart.getLocals().set( twoslotoffset + j + (mg.isStatic() ? 0 : 1), argtypes[j]); if (argtypes[j].getSize() == 2) { twoslotoffset++; fStart.getLocals().set( twoslotoffset + j + (mg.isStatic() ? 0 : 1), Type.UNKNOWN); } } InstructionContext start = cfg.contextOf(mg.getInstructionList() .getStart()); // don't need to compare for first frame inFrames.put(start, fStart); boolean fbool = start.execute(fStart, new ArrayList(), icv, ev); Frame fout = start.getOutFrame(new ArrayList()); outFrames.put(start,fout); start.setTag(start.getTag() + 1); // int posnow = start.getInstruction().getPosition(); // osa[start.getInstruction().getPosition()].add(fStart.getStack() // .getClone()); // lva[start.getInstruction().getPosition()].add(fStart.getLocals() // .getClone()); Vector ics = new Vector(); // Type: InstructionContext Vector ecs = new Vector(); // Type: ArrayList (of // InstructionContext) ics.add(start); ecs.add(new ArrayList()); int loopcnt = 1; // LOOP! while (!ics.isEmpty()) { loopcnt++; InstructionContext u; ArrayList ec; u = (InstructionContext) ics.get(0); //System.out.println(u.toString()); ec = (ArrayList) ecs.get(0); ics.remove(0); ecs.remove(0); ArrayList oldchain = (ArrayList) (ec.clone()); ArrayList newchain = (ArrayList) (ec.clone()); newchain.add(u); if ((u.getInstruction().getInstruction()) instanceof RET) { // We can only follow _one_ successor, the one after the // JSR that was recently executed. RET ret = (RET) (u.getInstruction().getInstruction()); ReturnaddressType t = (ReturnaddressType) u.getOutFrame( oldchain).getLocals().get(ret.getIndex()); InstructionContext theSuccessor = cfg.contextOf(t .getTarget()); // Sanity check InstructionContext lastJSR = null; int skip_jsr = 0; for (int ss = oldchain.size() - 1; ss >= 0; ss--) { if (skip_jsr < 0) { throw new AssertionViolatedException( "More RET than JSR in execution chain?!"); } if (((InstructionContext) oldchain.get(ss)) .getInstruction().getInstruction() instanceof JsrInstruction) { if (skip_jsr == 0) { lastJSR = (InstructionContext) oldchain.get(ss); break; } else { skip_jsr--; } } if (((InstructionContext) oldchain.get(ss)) .getInstruction().getInstruction() instanceof RET) { skip_jsr++; } } if (lastJSR == null) { throw new AssertionViolatedException( "RET without a JSR before in ExecutionChain?! EC: '" + oldchain + "'."); } JsrInstruction jsr = (JsrInstruction) (lastJSR .getInstruction().getInstruction()); if (theSuccessor != (cfg.contextOf(jsr.physicalSuccessor()))) { throw new AssertionViolatedException("RET '" + u.getInstruction() + "' info inconsistent: jump back to '" + theSuccessor + "' or '" + cfg.contextOf(jsr.physicalSuccessor()) + "'?"); } if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { ics.add(theSuccessor); ecs.add((ArrayList) newchain.clone()); } //inFrames.put(theSuccessor,u.getOutFrame(oldchain)); theSuccessor.setTag(theSuccessor.getTag() + 1); // osa[theSuccessor.getInstruction().getPosition()].add(fStart // .getStack().getClone()); // lva[theSuccessor.getInstruction().getPosition()].add(fStart // .getLocals().getClone()); Frame prevf = (Frame)inFrames.put(theSuccessor,u.getOutFrame(oldchain)); Frame newf = theSuccessor.getOutFrame(newchain); Frame prevof = (Frame)outFrames.put(theSuccessor,newf); if (prevof != null && !frmComp(prevof,newf,theSuccessor)){ System.out.println("A: Gosling violation:" + prevf.toString()+newf.toString()); System.exit(-1); } } else {// "not a ret" // Normal successors. Add them to the queue of successors. // TODO: Does u get executed? InstructionContext[] succs = u.getSuccessors(); // System.out.println("suss#:" + succs.length); for (int s = 0; s < succs.length; s++) { InstructionContext v = succs[s]; //System.out.println(v.toString()); if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { ics.add(v); ecs.add((ArrayList) newchain.clone()); } v.setTag(v.getTag() + 1); // osa[v.getInstruction().getPosition()].add(fStart // .getStack().getClone()); // lva[v.getInstruction().getPosition()].add(fStart // .getLocals().getClone()); Frame prevf = (Frame)inFrames.put(v,u.getOutFrame(oldchain)); Frame newf = v.getOutFrame(newchain); Frame prevof = (Frame)outFrames.put(v,newf); if (prevof != null && !frmComp(prevof,newf,v)){ System.out.println("B: Gosling violation:" + fStart.toString()); System.exit(-1); } } }// end "not a ret" // Exception Handlers. Add them to the queue of successors. // [subroutines are never protected; mandated by JustIce] ExceptionHandler[] exc_hds = u.getExceptionHandlers(); for (int s = 0; s < exc_hds.length; s++) { InstructionContext v = cfg.contextOf(exc_hds[s] .getHandlerStart()); Frame f = new Frame( u.getOutFrame(oldchain).getLocals(), new OperandStack( u.getOutFrame(oldchain).getStack() .maxStack(), (exc_hds[s].getExceptionType() == null ? Type.THROWABLE : exc_hds[s].getExceptionType()))); if (v.execute(f, new ArrayList(), icv, ev)) { ics.add(v); ecs.add(new ArrayList()); } v.setTag(v.getTag() + 1); // osa[v.getInstruction().getPosition()].add(fStart.getStack() // .getClone()); // lva[v.getInstruction().getPosition()].add(fStart // .getLocals().getClone()); Frame prevf = (Frame)inFrames.put(v,f); Frame newf = v.getOutFrame(new ArrayList()); Frame prevof = (Frame)outFrames.put(v,newf); if (prevof != null && !frmComp(prevof,newf,v)){ System.err.println("C: Gosling violation:" + prevf.toString()+newf.toString()); System.exit(-1); } } }// while (!ics.isEmpty()) END InstructionHandle ih = start.getInstruction(); //Check that all instruction have been simulated do{ InstructionContext ic = cfg.contextOf(ih); if(ic.getTag() == 0){ System.err.println("Instruction "+ic.toString()+" not simulated."); System.exit(-1); } }while((ih = ih.getNext()) != null); // int inFramesSize = inFrames.size(); // System.out.println("numInstructions:"+instCnt+" // inFramesSize:"+inFramesSize); // This array holds the operand type bits in the low bits ogci = new int[instCnt]; // This array holds the local type bits in the low bits mgci = new int[instCnt]; pcinfo = new String[instCnt]; int oldPC = 0; int PC = 0; int icnt = 0; // for debug info StringBuffer ogciStr = new StringBuffer(); ih = start.getInstruction(); do { oldPC = PC; PC = ih.getPosition(); // Pad up the operands with the same ref bits when the PC gets // ahead // it will either save time if a linked list is used or not be // used for anything // at run time as the PC will not point to the padded positions. for (int i = oldPC + 1; i < PC; i++) { // System.out.println("Padding up: // ogci["+i+"]=ogci["+oldPC+"]"); ogci[i] = ogci[oldPC]; mgci[i] = mgci[oldPC]; } ogciStr.append("ih:" + ih.toString()); pcinfo[PC] = ih.toString(); InstructionContext ic = cfg.contextOf(ih); // System.out.println(ih.toString()+" tag:"+ic.getTag()); // It is here the incoming frame is used to achieve the desired // PC->operand mapping Frame f1 = (Frame) inFrames.get(ic); LocalVariables lvs = f1.getLocals(); // mapping the all the locals for this value of the PC for (int i = 0; i < lvs.maxLocals(); i++) { if (lvs.get(i) instanceof ReferenceType) { int ref = (1 << i); mgci[PC] |= ref; } if (lvs.get(i) instanceof UninitializedObjectType) { // this.addMessage("Warning: ReturnInstruction '"+ic+"' // may leave method with an uninitialized object in the // local variables array '"+lvs+"'."); } } OperandStack os = f1.getStack(); // System.out.println("-->ih["+PC+"]: line"+ih.toString()+" // icnt:"+icnt); // System.out.println(" pre"); // System.out.println(" os slots:"+os.slotsUsed()); ogciStr.append(",os slots:" + os.slotsUsed()); // Each slot is 32 bits int j = 0; // Used to offset for LONG and DOUBLE // mapping the operands for this value of the PC for (int i = 0; i < os.slotsUsed(); i++, j++) { // System.out.println(" // op["+i+"]:"+(os.peek(j)).getSignature()); ogciStr.append(",operand(" + i + "):" + (os.peek(j)).getSignature()); if (os.peek(j) instanceof UninitializedObjectType) { // System.out.println("Warning: ReturnInstruction // '"+ic+"' may leave method with an uninitialized // object on the operand stack '"+os+"'."); } if (os.peek(j) instanceof ReferenceType) { int ref = (1 << i); ogci[PC] |= ref; } // peek() is per type if (os.peek(j).getSignature().equals("J") || os.peek(j).getSignature().equals("D")) { j--; } } // System.out.println(" ogci["+PC+"]:"+bitStr(ogci[PC])); // This frame is post (after) the instruction and thus not what // we need Frame f2 = ic.getOutFrame(new ArrayList()); LocalVariables lvs2 = f2.getLocals(); int ogci2 = 0; for (int i = 0; i < lvs2.maxLocals(); i++) { if (lvs2.get(i) instanceof UninitializedObjectType) { // System.out.println("Warning: ReturnInstruction // '"+ic+"' may leave method with an uninitialized // object in the local variables array '"+lvs+"'."); } } OperandStack os2 = f2.getStack(); // System.out.println(" post"); // System.out.println(" os slots:"+os2.slotsUsed()); // Each slot is 32 bits j = 0; // Used to offset for LONG and DOUBLE for (int i = 0; i < os2.slotsUsed(); i++, j++) { // System.out.println(" // op["+i+"]:"+(os2.peek(j)).getSignature()); // ogciStr.append(",operand("+i+"):"+(os2.peek(j)).getSignature()); if (os2.peek(j) instanceof UninitializedObjectType) { // System.out.println("Warning: ReturnInstruction // '"+ic+"' may leave method with an uninitialized // object on the operand stack '"+os+"'."); } if (os2.peek(j) instanceof ReferenceType) { int ref = (1 << i); ogci2 |= ref; } // peek() is per type if (os2.peek(j).getSignature().equals("J") || os2.peek(j).getSignature().equals("D")) { j--; } } // System.out.println(" ogci["+PC+"]:"+bitStr(ogci2)); icnt++; // System.out.println(""); } while ((ih = ih.getNext()) != null); // pad up the end for (int i = (PC + 1); i < instCnt; i++) { // System.out.println("Post padding up: // ogci["+i+"]=ogci["+PC+"]"); ogci[i] = ogci[PC]; mgci[i] = mgci[PC]; } // Good for debugging // System.out.println(" *** // "+mi.cli.clazz.getClassName()+"."+mi.methodId+" --> mreallocals // ="+mreallocals+" margs ="+margs+" mstack ="+mstack); for (int i = 0; i < instCnt; i++) { // System.out.println(pcinfo[i]+" ogci["+i+"]"+bitStr(ogci[i])); // System.out.println(pcinfo[i]+" mgci["+i+"]"+bitStr(mgci[i])); } // System.out.println("--"); // calculate length for raw method int varcnt = mstack + mreallocals + margs; int pos = instCnt * varcnt; length = pos / WORDLEN; // whole words if ((pos % WORDLEN) > 0) { length++; // partly used word } length++; // gcpack } // if has code } /** * Compares the operands of two frames. It will detect the rare event that * the Gosling property is violated from two jsr instructions reaching the * same code but with different operand or local signatures. Operands are * checked for fun even though they must obey the Gosling property. TODO: * Implement code that can split local variables if a violation occurs. * * @param prevf * the previous frame if it has been visited before * @param newf * the next frame * @return true if the frames equal or if prevf == null */ boolean frmComp(Frame prevf, Frame newf, InstructionContext ic){ boolean res = true; LocalVariables lvprev = prevf.getLocals(); LocalVariables lvnew = newf.getLocals(); OperandStack opprev = prevf.getStack(); OperandStack opnew = newf.getStack(); if(opprev.slotsUsed()!=opnew.slotsUsed()){ res = false; System.out.println("OperandStack size does not equal new OperandStack."+ prevf.toString()+newf.toString()+ic.toString()); } int j=0; for(int i=0;i<opprev.slotsUsed();i++, j++){ if(opprev.peek(j).getType()!=opnew.peek(j).getType()){ System.err.println("Operands differ "+opprev.peek(j).toString()+" "+ opnew.peek(j).toString()); res = false; } // peek() is per type if (opprev.peek(j).getSignature().equals("J") || opprev.peek(j).getSignature().equals("D")) { j--; } } if(lvprev.maxLocals()!=lvnew.maxLocals()){ res = false; System.out.println("MaxLocals LocalVariables does not equal new LocalVariables."+ prevf.toString()+newf.toString()+ic.toString()); } for(int i=0;i<lvprev.maxLocals();i++){ if(lvprev.get(i).getType()!=lvnew.get(i).getType()){ res = false; System.err.println("Local types differ "+lvprev.get(i).toString()+" "+ lvnew.get(i).toString()); } } // TODO: Why does this not work for RTThread? if(!opprev.equals(opnew)||!lvprev.equals(lvnew)){ // res = false; int stopit = 0; // System.out.println("Previous OperandStack or Localvars does not equal new OperandStack."+ // prevf.toString()+newf.toString()+ic.toString()); } return res; } /* boolean frmComp(Frame prevf, Frame newf, InstructionContext ic) { boolean res = true; if (prevf != null) { // check the operand stacks OperandStack osp = prevf.getStack(); OperandStack osn = newf.getStack(); if (osp.slotsUsed() != osn.slotsUsed()) { System.out .println("Frame OperandStack slotsUsed does not equal"); res = false; } int j = 0; // Used to offset for LONG and DOUBLE for (int i = 0; i < osp.slotsUsed(); i++, j++) { if (osp.peek(j).getSignature() != osn.peek(j).getSignature() && !(osp.peek(j).equals(BasicType.UNKNOWN) || osn.peek( j).equals(BasicType.UNKNOWN))) { System.out.println("Error: Signatures does not match"); res = false; } if ((osp.peek(j) instanceof UninitializedObjectType) != (osn .peek(j) instanceof UninitializedObjectType) && !(osp.peek(j).equals(BasicType.UNKNOWN) || osn.peek( j).equals(BasicType.UNKNOWN))) { System.out .println("Error: Operand stacks not equal for UninitializedObjectType"); res = false; } if ((osp.peek(j) instanceof ReferenceType) != (osn.peek(j) instanceof ReferenceType) && !(osp.peek(j).equals(BasicType.UNKNOWN) || osn.peek( j).equals(BasicType.UNKNOWN))) { System.out .println("Error: Operand stacks not equal for ReferenceType"); res = false; } // peek() is per type if (osp.peek(j).getSignature().equals("J") || osn.peek(j).getSignature().equals("D")) { j--; } } // check the locals LocalVariables lvsp = prevf.getLocals(); LocalVariables lvsn = newf.getLocals(); if (lvsp.maxLocals() != lvsn.maxLocals()) { System.out .println("Frame LocalVariables maxLocals does not equal"); res = false; } // mapping the all the locals for this value of the PC for (int i = 0; i < lvsp.maxLocals(); i++) { if (!lvsp.get(i).getSignature().equals( lvsn.get(i).getSignature()) && !(lvsp.get(i).equals(BasicType.UNKNOWN) || lvsn.get( i).equals(BasicType.UNKNOWN))) { System.out.println("Error: Local signatures for index " + i + " from prev frame does not match next frame"); System.out.println(lvsp.get(i).getSignature()); System.out.println(lvsn.get(i).getSignature()); res = false; } if (lvsp.get(i) instanceof ReferenceType != lvsn.get(i) instanceof ReferenceType && !(lvsp.get(i).equals(BasicType.UNKNOWN) || lvsn.get( i).equals(BasicType.UNKNOWN))) { System.out .println("Error: Local ref from prev frame does not match next frame"); res = false; } if (lvsp.get(i) instanceof UninitializedObjectType != lvsn .get(i) instanceof UninitializedObjectType && !(lvsp.get(i).equals(BasicType.UNKNOWN) || lvsn.get( i).equals(BasicType.UNKNOWN))) { System.out .println("Error: Local unit ref from prev frame does not match next frame"); res = false; } } } // prev != null if (res == false) { // print debug System.out.println(" *** " + mi.cli.clazz.getClassName() + "." + mi.methodId + " --> mreallocals =" + mreallocals + " margs =" + margs + " mstack =" + mstack); System.out.println("ic:" + ic.toString()); System.out.println("ih:" + ic.getInstruction().toString()); System.out.println("pc:" + ic.getInstruction().getPosition()); System.out.println("prev Frame:"); System.out.println(prevf); System.out.println("new Frame:"); System.out.println(newf); } return true; // return res; } */ /** * It dumps the local variable and stack operands GC info structures. Can be * called with out==null to get the word length. If the method is <code> * clinit</code> then the stackmap is not written out. */ public int dumpMethodGcis(PrintWriter out) { // System.out.println("dumpMethodGcis(PrintWriter out):"+out+", // mi.methodId:"+mi.methodId); int GCIHEADERLENGTH = 1; // key, gcpack // gcpack int UNICNTLEN = 10; // worst case if every PC has a unique pattern int INSTRLEN = 10; // 1024 instructions int MAXSTACKLEN = 5; // max 31 operands int MAXLOCALSLEN = 5; // max 31 args+locals int LOCALMARKLEN = 1; // 1 if local references int STACKMARKLEN = 1; // 1 if stack references int cntMgciWords = 0; int localmark = 0; int stackmark = 0; int ogcimark = 0; int mgcimark = 0; if(gcMethods != null && gcMethods.contains(method)){ if(out != null){ out.println("\n\t// no stackwalker info for " + mi.getCli().clazz.getClassName() + "." + mi.methodId +" because cfgReduce == true and gcMethods.contains(method)\n"); } return 0; } // if(mi.code != null){ // not abstract if (!method.isAbstract()) { if (instCnt != mgci.length || instCnt != ogci.length) { System.err .println("exit: instCnt!=mgci.length || instCnt!=ogci.length"); System.exit(-1); } if (out != null) { out.println("\n\t//stackwalker info for " + mi.getCli().clazz.getClassName() + "." + mi.methodId); } StringBuffer sb = new StringBuffer(); int mask = 0x01; int WORDLEN = 32; // raw packing int[] bitMap = new int[2 * instCnt]; StringBuffer[] bitInfo = new StringBuffer[2 * instCnt]; for (int i = 0; i < instCnt; i++) { bitInfo[i] = new StringBuffer(); bitInfo[i].append("[bit,pos,index,offset,mgci or ogci]:"); } int pos = 0; int index = 0; // raw patterns later used to determine unique patterns and the // index int pattern[] = new int[instCnt]; // pack bitMap for (int i = 0; i < instCnt; i++) { // used for Ref. reduction in paper if (mgci[i] > 0) { mgcimark = 1; } if (ogci[i] > 0) { ogcimark = 1; } // pack locals in high bits // pattern[i] is also done here for (int j = 0; j < (mreallocals + margs); j++) { int bit = (mgci[i] >>> j) & mask; pattern[i] |= bit << (j + mstack); // (pattern[i]<<1)| index = pos / WORDLEN; int offset = pos % WORDLEN; bit = bit << offset; bitMap[index] |= bit; bitInfo[index].append("[" + bit + "," + pos + "," + index + "," + offset + "," + "mgci[" + i + "][" + j + "]] "); pos++; } // pack stack in low bits for (int j = 0; j < mstack; j++) { int bit = (ogci[i] >>> j) & mask; pattern[i] |= bit << j; index = pos / WORDLEN; int offset = pos % WORDLEN; bit = bit << offset; bitMap[index] |= bit; bitInfo[index].append("[" + bit + "," + pos + "," + index + "," + offset + "," + "ogci[" + i + "][" + j + "]] "); pos++; } }// for // packing bitMap2 according to the reduced indexed method. // identify the unique patterns int unique = 0; int uniquepattern[] = new int[instCnt]; boolean match; for (int i = 0; i < instCnt; i++) { match = false; for (int j = 0; j < unique; j++) { if (pattern[i] == uniquepattern[j]) { match = true; break; } } if (!match) { uniquepattern[unique] = pattern[i]; // System.out.println("uniquepattern["+unique+"]="+bitStr(uniquepattern[unique])); unique++; } } // bits needed for unique patterns int patbits = unique * (mreallocals + margs + mstack); // used in a paper if (false) { // index2 reduction if (mgcimark == 0) { patbits -= unique * (mreallocals + margs); } if (ogcimark == 0) { patbits -= unique * (mstack); } } // count index bits int num = 1; int indexbits = 0; for (int i = 1; i < 32; i++) { if (num < unique) { num = (num << 1) | 1; } else { indexbits = i; break; } } // count bits used on indexing int pcbits = indexbits * instCnt; int totalbits = pcbits + patbits; // total words for bitmap int wdc = totalbits / 32; if (totalbits % 32 > 0) { wdc++; } // now make the indexed bitmaps int bitMap2[] = new int[wdc]; StringBuffer[] bitInfo2 = new StringBuffer[wdc]; for (int i = 0; i < wdc; i++) { bitInfo2[i] = new StringBuffer(); } int bitpos = 0; // bit map the indexes for (int i = 0; i < instCnt; i++) { match = false; for (int j = 0; j < unique; j++) { if (pattern[i] == uniquepattern[j]) { int control = 0; bitInfo2[bitpos / 32].append(" p" + i + "=up" + j + ":[" + bitpos + ":"); for (int k = 0; k < indexbits; k++) { int index2 = bitpos / 32; int offset2 = bitpos % 32; int bit = (j >>> k) & 0x01; control |= (bit << k); bitMap2[index2] |= (bit << offset2); bitInfo2[index2].append(bit); if (k == indexbits - 1) { bitInfo2[index2].append("]"); } bitpos++; } // System.out.println("pattern["+i+"]==uniquepattern["+j+"]"); // System.out.println("control:"+control); match = true; break; } } // for unique if (!match) { System.err.println("Problem with uniquematch for PC:" + i); System.exit(-1); } } // bit map the unique patterns for (int i = 0; i < unique; i++) { bitInfo2[bitpos / 32].append(" up" + i + ":[" + bitpos + ":"); for (int j = 0; j < (mreallocals + margs + mstack); j++) { int index2 = bitpos / 32; int offset2 = bitpos % 32; int bit = (uniquepattern[i] >>> j) & 0x01; bitMap2[index2] |= (bit << offset2); bitInfo2[index2].append(bit); if (j == (mreallocals + margs + mstack) - 1) { bitInfo2[index2].append("]"); } bitpos++; } } // pack gcpack int gcpack = unique; // unique patterns gcpack = (gcpack << INSTRLEN) | instCnt; gcpack = (gcpack << MAXSTACKLEN) | mstack; gcpack = (gcpack << MAXLOCALSLEN) | (mreallocals + margs); if ((mreallocals + margs) > 0) { localmark = 1; } // gcpack = (gcpack<<LOCALMARKLEN)|(localmark); // dump bit maps // even if no refs gcpack = (gcpack << LOCALMARKLEN) | (mgcimark); // only dump map // when a ref is // among the // operands "Ref. // only" in paper if (mstack > 0) { stackmark = 1; } // gcpack = (gcpack<<STACKMARKLEN)|(stackmark); // see above gcpack = (gcpack << STACKMARKLEN) | (ogcimark); // System.out.println("dumpMethodGcis Class // method:"+mi.cli.clazz.getClassName()+"."+mi.methodId); // System.out.println("patbits:"+patbits); // System.out.println("pcbits:"+pcbits); // System.out.println("unique:"+unique); // System.out.println("instCnt:"+instCnt); // System.out.println("indexbits:"+indexbits); // System.out.println("locals:"+(mreallocals+margs)); // System.out.println("localmark:"+localmark); // System.out.println("stack:"+mstack); // System.out.println("stackmark:"+stackmark); // System.out.println("index="+index+" // bitMap.length:"+bitMap.length+" instCnt:"+instCnt+" pos:"+pos); pcwords = 1; // set this to 0 or 1 depending if gcpack is written // out //Don't write stackmap for <clinit> methods if(method.getName().equals("<clinit>")){ gcpack = 0; localmark = 0; stackmark = 0; mgcimark = 0; ogcimark = 0; } // just check that the bitMap can be reconstructed from bitMap2 else if (!compareBitMap(bitMap, bitMap2, gcpack, mgci, ogci)) { System.err.println("Error in the bitmaps."); System.exit(-1); } // can also use localmark and stackmark here if bit maps are needed // regardless // of the presense of references if (instCnt > 0 && (mgcimark == 1 || ogcimark == 1)) { pcwords += (patbits + pcbits) / 32; if (((patbits + pcbits) % 32) > 0) { pcwords++; } // change flag below and also on in setMarkWords() in GCStkWalk if (true) { // write out indexed version for (int i = 0; i < wdc; i++) { if (out != null) { out.println("\t" + bitMap2[i] + ",//\tbitMap2[" + i + "]: " + bitInfo2[i].toString() + ", " + bitStr(bitMap2[i])); } cntMgciWords++; } //debug for (int i = 0; i < instCnt; i++) { if (out != null) { out.println("\t// mgci["+i+"]:"+bitStr(mgci[i])+" ogci["+i+"]:"+bitStr(ogci[i])); } } } else // write out raw version { for (int i = 0; i <= index; i++) { if (out != null) { // out.println("\t\t//\tbitMap["+i+"]:"+bitStr(bitMap[i])+" // "+bitInfo[i].toString()); out.println("\t\t" + bitMap[i] + ",//\tbitMap[" + i + "]:" + bitStr(bitMap[i])); } cntMgciWords++; } } } cntMgciWords += GCIHEADERLENGTH; if (out != null) { out.println("\t" + gcpack + ",//\tgcpack[0:stackmark=" + stackmark + ",1:localmark=" + localmark + ",2-6:maxlocals=" + (mreallocals + margs) + ",7-11:maxstack=" + mstack + ",12-21:instr=" + instCnt + ",22-31:unicnt=" + unique + "] indexbits(to index unique):" + indexbits + " gcpack:" + bitStr(gcpack)); } // System.out.println("cntMgciWords:"+cntMgciWords); // System.out.println("pcwords:"+pcwords); if (out != null) { totalpcwords += pcwords; totalcntMgciWords += cntMgciWords; } // System.out.println("totalpcwords:"+totalpcwords); // System.out.println("totalcntMgciWords:"+totalcntMgciWords); // System.out.println("--"); } // if not abstract // System.err.println("dumpMethodGcis // method:"+cli.clazz.getClassName()+"."+methodId); // System.err.println("out MethodInfo.cntMgci:"+MethodInfo.cntMgci+" // MethodInfo.cntMgciWords:"+MethodInfo.cntMgciWords); // System.err.println("index="+index+" bitMap.length:"+bitMap.length+" // instCnt:"+instCnt+" mreallocals:"+mreallocals+" margs:"+margs +" // mstack:"+mstack+" pos:"+pos); // System.err.println("method.isAbstract():"+method.isAbstract()); // System.err.println("..."); // System.out.println("cntMgciWords:"+cntMgciWords); return cntMgciWords; } /** * Called with a pc (program counter) value which will be removed from the * mgci and ogci structures. The <oode>instCnt</code> is also decremented by * one. * @param pc * @return true if pc<instCnt */ public void removePC(int pc){ if(gcMethods != null && gcMethods.contains(method)) return; int oldogci[] = ogci; int oldmgci[] = mgci; ogci = new int[instCnt-1]; mgci = new int[instCnt-1]; if(pc==0){ System.arraycopy(oldogci,1,ogci,0,instCnt-1); System.arraycopy(oldmgci,1,mgci,0,instCnt-1); } else if(pc==instCnt-1){ System.arraycopy(oldogci,0,ogci,0,instCnt-1); System.arraycopy(oldmgci,0,mgci,0,instCnt-1); } else{ System.arraycopy(oldogci,0,ogci,0,pc); System.arraycopy(oldogci,pc+1,ogci,pc,instCnt-1-pc); System.arraycopy(oldmgci,0,mgci,0,pc); System.arraycopy(oldmgci,pc+1,mgci,pc,instCnt-1-pc); } //reduce instcnt accordingly instCnt--; } // Sanity checking the bitmaps by unpacking both and comparing boolean compareBitMap(int bitMap[], int bitMap2[], int gcpack, int mcgi[], int ocgi[]) { boolean compareresult = true; int UNICNTLEN = 10; // worst case if every PC has a unique pattern int INSTRLEN = 10; // 1024 instructions int MAXSTACKLEN = 5; // max 31 operands int MAXLOCALSLEN = 5; // max 31 args+locals int LOCALMARKLEN = 1; // 1 if local references int STACKMARKLEN = 1; // 1 if stack references int unicnt = (gcpack >>> (INSTRLEN + MAXSTACKLEN + MAXLOCALSLEN + LOCALMARKLEN + STACKMARKLEN)) & 0x03ff; int instr = (gcpack >>> (MAXSTACKLEN + MAXLOCALSLEN + LOCALMARKLEN + STACKMARKLEN)) & 0x03ff; int maxstack = (gcpack >>> (MAXLOCALSLEN + LOCALMARKLEN + STACKMARKLEN)) & 0x1f; int maxlocals = (gcpack >>> (LOCALMARKLEN + STACKMARKLEN)) & 0x1f; int localmark = (gcpack >>> (STACKMARKLEN)) & 0x01; ; int stackmark = gcpack & 0x01; // System.out.println("unicnt:"+unicnt); // System.out.println("instr:"+instr); // System.out.println("maxstack:"+maxstack); // System.out.println("maxlocals:"+maxlocals); // System.out.println("localmark:"+localmark); // System.out.println("stackmark:"+stackmark); int num = 1; int indexbits = 0; for (int i = 1; i < 32; i++) { if (num < unicnt) { num = (num << 1) | 1; } else { indexbits = i; break; } } // reconstruct patterns from bitMap2 int patternindex2[] = new int[instr]; int unipattern[] = new int[unicnt]; int pos = 0; for (int i = 0; i < instr; i++) { patternindex2[i] = getBitsFromArray(bitMap2, pos, indexbits); pos += indexbits; } for (int i = 0; i < unicnt; i++) { unipattern[i] = getBitsFromArray(bitMap2, pos, (maxstack + maxlocals)); pos += (maxstack + maxlocals); } // bitmap1 patterns int patterns1[] = new int[instr]; pos = 0; for (int i = 0; i < instr; i++) { patterns1[i] = getBitsFromArray(bitMap, pos, (maxstack + maxlocals)); pos += (maxstack + maxlocals); if (patterns1[i] != unipattern[patternindex2[i]]) { // System.out.println("mgci["+i+"]:"+bitStr(mcgi[i])); // System.out.println("ocgi["+i+"]:"+bitStr(ocgi[i])); // System.out.println("patterns1["+i+"]:"+bitStr(patterns1[i])); // System.out.println("patternindex2["+i+"]:"+patternindex2[i]); // System.out.println("unipattern[patternindex2[["+i+"]]:"+bitStr(unipattern[patternindex2[i]])); compareresult = false; break; } } return compareresult; } // can return max 32 bits. int getBitsFromArray(int array[], int pos, int len) { if (len > 32) { System.out.println("len>32"); System.exit(-1); } if ((pos + len) > array.length * 32) { System.out.println("pos:" + pos + " len:" + len + " too big for array[].length:" + array.length); System.exit(-1); } int res = 0; for (int i = pos; i < pos + len; i++) { int index = i / 32; int offset = i % 32; int word = array[index]; res |= (word >> offset) & 0x01; } return res; } /** * Returns the length in words that the GC info wil consume. Called from * SetMethodInfo's visitJavaClass method. */ public int gcLength() { int len = -1; if(gcMethods != null && gcMethods.contains(method)){ len = 0; } else { len = dumpMethodGcis(null); } return len; } /** * Make a word into a 0/1 bit string. Note that the bit are written with the * low bits first. */ String bitStr(int word) { // make ogci[PC] to bit string for debugging StringBuffer sb = new StringBuffer(); int mask = 0x01; // for(int i = 31;i>=0;i--){ for (int i = 0; i < 32; i++) { int res = (word >>> i) & mask; if ((i + 1) % 8 == 0 && i < 31) { sb.append("_"); } sb.append(res); } return sb.toString(); } } /** * Extends org.apache.bcel.verifier.structurals.Frame just to get access to the * _this field, which the the operandWalker method in MethodInfo uses. */ class FrameFrame extends Frame { public FrameFrame(int maxLocals, int maxStack) { super(maxLocals, maxStack); } public FrameFrame(LocalVariables locals, OperandStack stack) { super(locals, stack); } void setThis(UninitializedObjectType uot) { _this = uot; } UninitializedObjectType getThis() { return _this; } } /** * BCEL throws an exception for the util.Dbg class because it overloads a field. * The choice (as described in the BCEL method comment for around line 2551 in * org.apache.bcel.verifier.structurals.InstConstraintVisitor) is to comment out * this check and recompile BCEL jar. Insted of recompiling BCEL the choice is * to override the methods, which is ok? as we are not using BCEL for bytecode * verification purposes (using Sun Javac). */ class AnInstConstraintVisitor extends InstConstraintVisitor { public void visitAALOAD(AALOAD o) { } public void visitAASTORE(AASTORE o) { } public void visitACONST_NULL(ACONST_NULL o) { } public void visitALOAD(ALOAD o) { } public void visitANEWARRAY(ANEWARRAY o) { } public void visitARETURN(ARETURN o) { } public void visitARRAYLENGTH(ARRAYLENGTH o) { } public void visitASTORE(ASTORE o) { } public void visitATHROW(ATHROW o) { } public void visitBALOAD(BALOAD o) { } public void visitBASTORE(BASTORE o) { } public void visitBIPUSH(BIPUSH o) { } public void visitBREAKPOINT(BREAKPOINT o) { } public void visitCALOAD(CALOAD o) { } public void visitCASTORE(CASTORE o) { } public void visitCHECKCAST(CHECKCAST o) { } public void visitCPInstruction(CPInstruction o) { } public void visitD2F(D2F o) { } public void visitD2I(D2I o) { } public void visitD2L(D2L o) { } public void visitDADD(DADD o) { } public void visitDALOAD(DALOAD o) { } public void visitDASTORE(DASTORE o) { } public void visitDCMPG(DCMPG o) { } public void visitDCMPL(DCMPL o) { } public void visitDCONST(DCONST o) { } public void visitDDIV(DDIV o) { } public void visitDLOAD(DLOAD o) { } public void visitDMUL(DMUL o) { } public void visitDNEG(DNEG o) { } public void visitDREM(DREM o) { } public void visitDRETURN(DRETURN o) { } public void visitDSTORE(DSTORE o) { } public void visitDSUB(DSUB o) { } public void visitDUP_X1(DUP_X1 o) { } public void visitDUP_X2(DUP_X2 o) { } public void visitDUP(DUP o) { } public void visitDUP2_X1(DUP2_X1 o) { } public void visitDUP2_X2(DUP2_X2 o) { } public void visitDUP2(DUP2 o) { } public void visitF2D(F2D o) { } public void visitF2I(F2I o) { } public void visitF2L(F2L o) { } public void visitFADD(FADD o) { } public void visitFALOAD(FALOAD o) { } public void visitFASTORE(FASTORE o) { } public void visitFCMPG(FCMPG o) { } public void visitFCMPL(FCMPL o) { } public void visitFCONST(FCONST o) { } public void visitFDIV(FDIV o) { } public void visitFieldInstruction(FieldInstruction o) { } public void visitFLOAD(FLOAD o) { } public void visitFMUL(FMUL o) { } public void visitFNEG(FNEG o) { } public void visitFREM(FREM o) { } public void visitFRETURN(FRETURN o) { } public void visitFSTORE(FSTORE o) { } public void visitFSUB(FSUB o) { } public void visitGETFIELD(GETFIELD o) { } public void visitGETSTATIC(GETSTATIC o) { } public void visitGOTO_W(GOTO_W o) { } public void visitGOTO(GOTO o) { } public void visitI2B(I2B o) { } public void visitI2C(I2C o) { } public void visitI2D(I2D o) { } public void visitI2F(I2F o) { } public void visitI2L(I2L o) { } public void visitI2S(I2S o) { } public void visitIADD(IADD o) { } public void visitIALOAD(IALOAD o) { } public void visitIAND(IAND o) { } public void visitIASTORE(IASTORE o) { } public void visitICONST(ICONST o) { } public void visitIDIV(IDIV o) { } public void visitIF_ACMPEQ(IF_ACMPEQ o) { } public void visitIF_ACMPNE(IF_ACMPNE o) { } public void visitIF_ICMPEQ(IF_ICMPEQ o) { } public void visitIF_ICMPGE(IF_ICMPGE o) { } public void visitIF_ICMPGT(IF_ICMPGT o) { } public void visitIF_ICMPLE(IF_ICMPLE o) { } public void visitIF_ICMPLT(IF_ICMPLT o) { } public void visitIF_ICMPNE(IF_ICMPNE o) { } public void visitIFEQ(IFEQ o) { } public void visitIFGE(IFGE o) { } public void visitIFGT(IFGT o) { } public void visitIFLE(IFLE o) { } public void visitIFLT(IFLT o) { } public void visitIFNE(IFNE o) { } public void visitIFNONNULL(IFNONNULL o) { } public void visitIFNULL(IFNULL o) { } public void visitIINC(IINC o) { } public void visitILOAD(ILOAD o) { } public void visitIMPDEP1(IMPDEP1 o) { } public void visitIMPDEP2(IMPDEP2 o) { } public void visitIMUL(IMUL o) { } public void visitINEG(INEG o) { } public void visitINSTANCEOF(INSTANCEOF o) { } public void visitInvokeInstruction(InvokeInstruction o) { } public void visitINVOKEINTERFACE(INVOKEINTERFACE o) { } public void visitINVOKESPECIAL(INVOKESPECIAL o) { } public void visitINVOKESTATIC(INVOKESTATIC o) { } public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o) { } public void visitIOR(IOR o) { } public void visitIREM(IREM o) { } public void visitIRETURN(IRETURN o) { } public void visitISHL(ISHL o) { } public void visitISHR(ISHR o) { } public void visitISTORE(ISTORE o) { } public void visitISUB(ISUB o) { } public void visitIUSHR(IUSHR o) { } public void visitIXOR(IXOR o) { } public void visitJSR_W(JSR_W o) { } public void visitJSR(JSR o) { } public void visitL2D(L2D o) { } public void visitL2F(L2F o) { } public void visitL2I(L2I o) { } public void visitLADD(LADD o) { } public void visitLALOAD(LALOAD o) { } public void visitLAND(LAND o) { } public void visitLASTORE(LASTORE o) { } public void visitLCMP(LCMP o) { } public void visitLCONST(LCONST o) { } public void visitLDC_W(LDC_W o) { } public void visitLDC(LDC o) { } public void visitLDC2_W(LDC2_W o) { } public void visitLDIV(LDIV o) { } public void visitLLOAD(LLOAD o) { } public void visitLMUL(LMUL o) { } public void visitLNEG(LNEG o) { } public void visitLoadClass(LoadClass o) { } public void visitLoadInstruction(LoadInstruction o) { } public void visitLocalVariableInstruction(LocalVariableInstruction o) { } public void visitLOOKUPSWITCH(LOOKUPSWITCH o) { } public void visitLOR(LOR o) { } public void visitLREM(LREM o) { } public void visitLRETURN(LRETURN o) { } public void visitLSHL(LSHL o) { } public void visitLSHR(LSHR o) { } public void visitLSTORE(LSTORE o) { } public void visitLSUB(LSUB o) { } public void visitLUSHR(LUSHR o) { } public void visitLXOR(LXOR o) { } public void visitMONITORENTER(MONITORENTER o) { } public void visitMONITOREXIT(MONITOREXIT o) { } public void visitMULTIANEWARRAY(MULTIANEWARRAY o) { } public void visitNEW(NEW o) { } public void visitNEWARRAY(NEWARRAY o) { } public void visitNOP(NOP o) { } public void visitPOP(POP o) { } public void visitPOP2(POP2 o) { } public void visitPUTFIELD(PUTFIELD o) { } public void visitPUTSTATIC(PUTSTATIC o) { } public void visitRET(RET o) { } public void visitRETURN(RETURN o) { } public void visitReturnInstruction(ReturnInstruction o) { } public void visitSALOAD(SALOAD o) { } public void visitSASTORE(SASTORE o) { } public void visitSIPUSH(SIPUSH o) { } public void visitStackConsumer(StackConsumer o) { } public void visitStackInstruction(StackInstruction o) { } public void visitStackProducer(StackProducer o) { } public void visitStoreInstruction(StoreInstruction o) { } public void visitSWAP(SWAP o) { } public void visitTABLESWITCH(TABLESWITCH o) { } }