001: /*
002: * @(#)ModifiedInstructionList.java
003: *
004: * Copyright (C) 2002-2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.compiler;
028:
029: import java.util.ArrayList;
030: import java.util.Iterator;
031: import java.util.List;
032:
033: import org.apache.bcel.generic.InstructionHandle;
034: import org.apache.bcel.generic.InstructionList;
035:
036: /**
037: * Refers to a class file that has been modified with additional logging
038: * statements.
039: * <P>
040: * As of January 22, 2003, the appending of probes to the end of the
041: * instruction list is no longer supported; supporting it would mean that
042: * instructions will be added to a method after the final "return" calls,
043: * which might cause class file validation errors.
044: *
045: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
046: * @version $Date: 2004/04/15 05:48:25 $
047: * @since December 17, 2002
048: */
049: public class ModifiedInstructionList {
050: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
051: .getLogger(ModifiedInstructionList.class);
052: private boolean closed = false;
053: private InstructionList modList; // this is the actual original list
054: private List marked;
055:
056: /**
057: * constant pool index for the name of the class's signature.
058: */
059: private int classSigPoolIndex;
060:
061: /**
062: * constant pool index for the method-ref for invoking the logger.
063: */
064: private int staticMethodPoolIndex;
065:
066: /**
067: * Reference to the owning method's index
068: */
069: private short methodIndex;
070:
071: /**
072: * Keep track of the original instructions vs. their replacement for
073: * target heads and tails.
074: */
075: private ModifiedTargeters targeters;
076:
077: /**
078: * @throws IllegalStateException if the class file has already been
079: * modified (identified by a class name field).
080: */
081: ModifiedInstructionList(short methodIndex, int classSigPoolIndex,
082: int staticMethodPoolIndex, InstructionList list,
083: ModifiedTargeters mt) {
084: if (list == null || mt == null) {
085: throw new IllegalArgumentException("no null args");
086: }
087: this .methodIndex = methodIndex;
088: this .classSigPoolIndex = classSigPoolIndex;
089: this .staticMethodPoolIndex = staticMethodPoolIndex;
090:
091: if (!isValidInstructionList(list)) {
092: throw new IllegalArgumentException(
093: "Instruction list contains "
094: + "unsupported instruction setup.");
095: }
096: this .modList = list;
097: this .targeters = mt;
098:
099: // there is a bug in the list.copy() that causes some
100: // LOOKUPSWITCH instructions to become corrupted in the original!
101: setupMarkList();
102: }
103:
104: //------------------------------------------------------------------------
105:
106: public static boolean isValidInstructionList(InstructionList list) {
107: try {
108: list.getByteCode();
109: } catch (Exception ex) {
110: return false;
111: }
112: return true;
113: }
114:
115: /**
116: * This method can safely be called multiple times.
117: *
118: * Bug: We need to keep track of the original handle, and the corresponding
119: * new handle that goes before it, so that we can go back and recreate the
120: * CodeException objects for the method.
121: */
122: void updateInstructionList() {
123: checkClose();
124:
125: LOG.debug("********************************");
126: Iterator iter = this .marked.iterator();
127: while (iter.hasNext()) {
128: MarkedInstruction mi = (MarkedInstruction) iter.next();
129:
130: InstructionList list = mi.getMarkedList();
131: if (list != null && list.getLength() > 0) {
132: InstructionHandle instr = mi.getHandle();
133: LOG.debug("Adding list (length = " + list.getLength()
134: + ", value='" + list + "') before handle '"
135: + instr + "'.");
136: InstructionHandle probe;
137: if (mi.getInstruction() instanceof NullInstruction) {
138: // the null instruction will not add itself, but will
139: // append its marks to the end of the method.
140: /*
141: LOG.debug( "Appending probe to end of instructions." );
142: probe = this.modList.append( list );
143: this.targeters.appendProbe( probe );
144: */
145: LOG
146: .warn("Appending probes to end of instructions is no longer allowed");
147: } else {
148: // normal instructions will insert their marks before
149: // the instruction is executed.
150: LOG.debug("Inserting probe before [" + instr + "]");
151: probe = this .modList.insert(instr, list);
152: this .targeters.insertProbe(instr, probe);
153: }
154: }
155: }
156: this .targeters.update();
157: LOG.debug("Final modified list = '" + this .modList + "'");
158: }
159:
160: public int getInstructionCount() {
161: checkClose();
162:
163: // ignore the final NullInstruction
164: return this .marked.size() - 1;
165: }
166:
167: public MarkedInstruction getInstructionAt(int index) {
168: checkClose();
169:
170: return (MarkedInstruction) this .marked.get(index);
171: }
172:
173: void close() {
174: checkClose();
175:
176: this .closed = true;
177: this .modList.dispose();
178: this .marked = null;
179: this .modList = null;
180: }
181:
182: //------------------------------------------------------------------------
183:
184: private void setupMarkList() {
185: this .marked = new ArrayList();
186: //Iterator iter = this.instructions.iterator();
187: Iterator iter = this .modList.iterator();
188: while (iter.hasNext()) {
189: InstructionHandle ih = (InstructionHandle) iter.next();
190: this .marked.add(new MarkedInstruction(this .methodIndex,
191: this .classSigPoolIndex, this .staticMethodPoolIndex,
192: ih));
193: }
194:
195: // add in the final instruction to allow for marking the end
196: // of the method.
197: InstructionList il = new InstructionList();
198: InstructionHandle ih = il.append(new NullInstruction());
199: this .marked
200: .add(new MarkedInstruction(this .methodIndex,
201: this .classSigPoolIndex,
202: this .staticMethodPoolIndex, ih));
203: }
204:
205: private void checkClose() {
206: if (this .closed) {
207: throw new IllegalStateException("Method has been closed.");
208: }
209: }
210: }
|