001: /* GraphAction.java */
002: package org.quilt.cover.stmt;
003:
004: import org.apache.bcel.Constants;
005: import org.apache.bcel.classfile.ConstantPool;
006: import org.apache.bcel.generic.*;
007: import org.quilt.cl.*;
008: import org.quilt.graph.*;
009:
010: /**
011: * Walk the control flow graph, adding counter vertices on all edges
012: * inbound to code vertices. XXX Except those likely to cause
013: * problems; need to review this code later to make sure that all
014: * possibilities are covered.
015: *
016: * @author <a href="mailto:jddixon@users.sourceforge.net">Jim Dixon</a>
017: */
018: public class GraphAction implements GraphXformer {
019:
020: private static StmtRegistry stmtReg = null;
021: private ClassGen clazz_ = null;
022: private MethodGen method_ = null;
023:
024: /** Processor name for use in reports. */
025: private static String name_ = null;
026:
027: private InstructionFactory factory_;
028:
029: private ConstantPoolGen cpGen_;
030:
031: /** Number of counters added to the graph. */
032: private int counterCount = 0;
033:
034: /** The class Xformer responsible for tracking counter counts */
035: private ClassAction classAct_ = null;
036:
037: /** constructor. */
038: public GraphAction() {
039: }
040:
041: public GraphAction(StmtRegistry reg) {
042: stmtReg = reg;
043: setName(this .getClass().getName());
044: }
045:
046: /**
047: * Constructor used by StmtRegistry. XXX Deprecate, dropping
048: * classAct argument. (Pass via registry if it's really necessary.)
049: *
050: * @param classAct The Xformer at the class level; the method needs
051: * to report back to it.
052: */
053: public GraphAction(StmtRegistry reg, ClassAction classAct) {
054: if (reg == null || classAct == null) {
055: throw new IllegalArgumentException("null argument");
056: }
057: stmtReg = reg;
058: classAct_ = classAct;
059: setName(this .getClass().getName());
060: }
061:
062: /** The visitor that actually adds the vertices to the graph. */
063: private class LampLighter implements org.quilt.graph.Visitor,
064: org.apache.bcel.Constants,
065: org.apache.bcel.generic.InstructionConstants {
066: private ControlFlowGraph graph = null;
067:
068: public LampLighter() {
069: }
070:
071: public void discoverGraph(Directed g) {
072: graph = (ControlFlowGraph) g;
073: }
074:
075: public void discoverVertex(Vertex v) {
076: }
077:
078: /**
079: * Instrument edges inbound to code vertex.
080: */
081: public void discoverEdge(Edge e) {
082: Vertex source = e.getSource();
083: Vertex target = e.getTarget();
084: ControlFlowGraph srcGraph = (ControlFlowGraph) source
085: .getGraph();
086: ControlFlowGraph tgtGraph = (ControlFlowGraph) target
087: .getGraph();
088: // DEBUG
089: if (tgtGraph != graph) {
090: System.out.println("GraphAction.discoverEdge:\n"
091: + " current graph is " + graph.getIndex()
092: + " but edge is " + e);
093: }
094: // END
095: boolean addingCounter = true;
096: if ((target instanceof CodeVertex)
097: && !(target instanceof CounterVertex)
098: && !(source instanceof CounterVertex)) {
099: Instruction srcConnInst = null;
100: if (source instanceof CodeVertex) {
101: srcConnInst = ((CodeVertex) source).getConnInst();
102: if (srcConnInst != null
103: && srcConnInst instanceof GotoInstruction
104: && (source.getGraph() != tgtGraph)) {
105: // Don't add a counter if the goto jumps out of
106: // this graph. XXX needs more thought! -- this
107: // eliminates some test failures but will fail to
108: // count some statement hits
109: addingCounter = false;
110: }
111: }
112: // XXX A BIT OF A KLUDGE (NestedTryBlocks bugfix)
113: if (srcGraph != graph) {
114: System.out
115: .println("GraphAction.discoverEdge WARNING: "
116: + "graph index "
117: + graph.getIndex()
118: + "\n but edge is "
119: + e
120: + " - not adding counter");
121: addingCounter = false;
122: }
123: if (addingCounter) {
124: CounterVertex cv;
125: if (source instanceof Exit) {
126: // MOVED UP
127: // ControlFlowGraph tgtGraph
128: // = (ControlFlowGraph)target.getGraph();
129: cv = (CounterVertex) tgtGraph
130: .insertCodeVertex(
131: (CodeVertex) new CounterVertex(
132: tgtGraph), e);
133:
134: } else {
135: cv = (CounterVertex) graph.insertCodeVertex(
136: (CodeVertex) new CounterVertex(graph),
137: e);
138: }
139: InstructionList ilist = cv.getInstructionList();
140: InstructionHandle ih = ilist.append(factory_
141: .createFieldAccess(clazz_.getClassName(),
142: "q$$q", new ArrayType(Type.INT, 1),
143: Constants.GETSTATIC));
144: ilist.append(new PUSH(cpGen_, counterCount++));
145: ilist.append(InstructionConstants.DUP2);
146: ilist.append(InstructionConstants.IALOAD);
147: ilist.append(new PUSH(cpGen_, 1));
148: ilist.append(InstructionConstants.IADD);
149: ilist.append(InstructionConstants.IASTORE);
150: // end instruction list ///////////////////
151:
152: // RETARGETING //////////////////////////////////////
153: if (source instanceof CodeVertex) {
154: if (srcConnInst != null) {
155: // UNARY ////////////////////////////////////
156:
157: // BINARY ///////////////////////////////////
158: if (srcConnInst instanceof GotoInstruction) {
159: if (!(e == source.getEdge())) {
160: ((CodeVertex) source).moveGoto(cv);
161: }
162: } else if (srcConnInst instanceof IfInstruction
163: || srcConnInst instanceof JsrInstruction) {
164: Edge otherEdge = ((BinaryConnector) source
165: .getConnector()).getOtherEdge();
166: if (e == otherEdge) {
167: // this is the 'else' branch; the
168: // counter needs to have a Goto
169: // connecting instruction
170: BranchInstruction bi = (BranchInstruction) srcConnInst;
171: InstructionHandle targ = bi
172: .getTarget();
173: bi.setTarget(ih);
174: cv.setConnInst(new GOTO(targ));
175: // ignore the edge returned
176: cv.makeBinary();
177: BinaryConnector bc = (BinaryConnector) cv
178: .getConnector();
179: Vertex myTarget = bc.getTarget();
180: // main edge is the flow-through exit
181: // DEBUG
182: if (graph.getExit() == null) {
183: System.out
184: .println("GRAPH HAS NULL EXIT");
185: }
186: // END
187: bc.setTarget(graph.getExit());
188: bc.setOtherTarget(myTarget);
189: }
190: }
191: // COMPLEX - Select /////////////////////////
192:
193: } // if srcConnInst != null
194: }
195: } // GEEP
196: // ELSE IF ENTRY ... ////////////////////////////////
197: }
198: }
199:
200: public void finishEdge(Edge e) {
201: }
202:
203: public void finishVertex(Vertex v) {
204: }
205:
206: public void finishGraph(Directed g) {
207: }
208: }
209:
210: // Apply the transformation to the graph.
211: public void xform(final ClassGen cg, final MethodGen method,
212: final ControlFlowGraph cfg) {
213: clazz_ = cg;
214: method_ = method;
215: cpGen_ = cg.getConstantPool();
216: factory_ = new InstructionFactory(clazz_, cpGen_);
217:
218: String className = clazz_.getClassName();
219: Ephemera eph = stmtReg.getEphemera(className);
220: // DEBUG
221: if (eph == null)
222: System.out.println("GraphAction.xform: eph is null!");
223: // END
224: counterCount = eph.getCounterCount();
225:
226: int cfgSize = cfg.size();
227: Walker walker = new Walker();
228: walker.visit(cfg, new LampLighter());
229:
230: // unnecessary, I think, because the ConstantPoolGen is part
231: // of the ClassGen
232: // clazz_.setConstantPool(cpGen_.getFinalConstantPool());
233:
234: eph.setEndCount(method.getName(), counterCount);
235:
236: }
237:
238: // GET/SET METHODS //////////////////////////////////////////////
239: /** Get the name for the transformation. */
240: public static String getName() {
241: return name_;
242: }
243:
244: /**
245: * Set a name for the transformation, to allow reports to refer
246: * to it.
247: */
248: public static void setName(String name) {
249: name_ = name;
250: }
251: }
|