001: /*
002: * @(#)DefaultMethodCode.java
003: *
004: * Copyright (C) 2002,2003 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 net.sourceforge.groboutils.codecoverage.v2.IAnalysisMetaData;
030: import net.sourceforge.groboutils.codecoverage.v2.IAnalysisModule;
031: import net.sourceforge.groboutils.codecoverage.v2.IMethodCode;
032: import net.sourceforge.groboutils.codecoverage.v2.datastore.ClassRecord;
033: import net.sourceforge.groboutils.codecoverage.v2.datastore.MarkRecord;
034:
035: import org.apache.bcel.classfile.LineNumberTable;
036: import org.apache.bcel.classfile.Method;
037: import org.apache.bcel.generic.Instruction;
038:
039: /**
040: * Implements the per-module interface to the method's code and marking
041: * the code. Controls the module's index and the current module's mark
042: * index for this method.
043: *
044: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
045: * @author Stefano Turri (stefano.turri AT it.ibm.com)
046: * @version $Date: 2004/04/20 23:12:06 $
047: * @since December 17, 2002
048: */
049: public class DefaultMethodCode implements IMethodCode {
050: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
051: .getLogger(DefaultMethodCode.class);
052:
053: private final ModifiedMethod method;
054: private final ModifiedInstructionList list;
055: private final String className;
056: private final String methodName;
057: private final IAnalysisModule analysisModule;
058: private final short measureIndex;
059: private final ClassRecord cr;
060: private short markCount = 0;
061:
062: DefaultMethodCode(short measureIndex, ModifiedMethod mm,
063: ClassRecord cr) {
064: if (mm == null || cr == null) {
065: throw new IllegalArgumentException("no null args");
066: }
067:
068: this .method = mm;
069: this .list = mm.getInstructionList();
070: if (this .list == null) {
071: throw new IllegalStateException("abstract method " + mm);
072: }
073: this .className = mm.getOriginalClass().getClassName();
074: this .methodName = mm.getMethodName();
075: this .measureIndex = measureIndex;
076: this .cr = cr;
077: this .analysisModule = this .cr.getAnalysisModuleSet()
078: .getAnalysisModuleAt(measureIndex);
079:
080: // ensure the class record is kosher
081: if (!this .className.equals(cr.getClassName())) {
082: throw new IllegalArgumentException(
083: "modified method class (" + this .className
084: + ") and class record class name ("
085: + cr.getClassName() + ") do not match: ");
086: }
087: if (cr.getMethodIndex(this .methodName) < 0) {
088: throw new IllegalArgumentException("method name ("
089: + this .methodName
090: + ") and class record do not match");
091: }
092: }
093:
094: /**
095: * Returns the original BCEL Method object.
096: *
097: * @return the original BCEL Method object
098: */
099: public Method getOriginalMethod() {
100: return this .method.getOriginalMethod();
101: }
102:
103: /**
104: * Returns the line number table for the original BCEL method object.
105: *
106: * @return the LineNumberTable, or <tt>null</tt> if there isn't one
107: * for this method.
108: */
109: public LineNumberTable getLineNumberTable() {
110: return getOriginalMethod().getLineNumberTable();
111: }
112:
113: /**
114: * A helper to get the method name.
115: *
116: * @return the method name
117: */
118: public String getMethodName() {
119: return this .methodName;
120: }
121:
122: /**
123: * A helper to get the class name.
124: *
125: * @return the class name
126: */
127: public String getClassName() {
128: return this .className;
129: }
130:
131: /**
132: * Returns the number of bytecode instructions in the method.
133: *
134: * @return the number of bytecode instructions
135: */
136: public int getInstructionCount() {
137: return this .list.getInstructionCount();
138: }
139:
140: /**
141: * Returns the bytecode instruction at the given index. If the index
142: * is out of range (< 0 or >= <tt>getInstructionCount()</tt>),
143: * then a <tt>IndexOutOfBoundsException</tt> is thrown.
144: *
145: * @param index the 0-based index of the method's instruction list
146: * @return the instruction at <tt>index</tt>
147: */
148: public Instruction getInstructionAt(int index) {
149: // even though the list allows us to get the "last" instruction,
150: // don't allow the user to do this.
151: if (index == getInstructionCount()) {
152: throw new IndexOutOfBoundsException(
153: "Even though you can put a mark at one more than the last "
154: + "instruction, you cannot retrieve any such instruction.");
155: }
156:
157: MarkedInstruction mi = this .list.getInstructionAt(index);
158: return mi.getInstruction();
159: }
160:
161: /**
162: * Marks an instruction for coverage analysis. If the index
163: * is out of range (< 0 or > <tt>getInstructionCount()</tt>),
164: * then a <tt>IndexOutOfBoundsException</tt> is thrown. Marks are
165: * added before the instruction at the given index is executed. Note that
166: * to mark the end of the method, you should use index equal to
167: * <tt>getInstructionCount()</tt>
168: *
169: * @param index the 0-based index of the method's instruction list
170: * @param meta meta-data the analysis module records in association with
171: * the mark. This cannot be <tt>null</tt>.
172: */
173: public synchronized void markInstruction(int index,
174: IAnalysisMetaData meta) {
175: if (meta == null) {
176: throw new IllegalArgumentException("no null args");
177: }
178: short count = this .markCount;
179: ++this .markCount;
180:
181: LOG.debug("Mark " + this + " at instruction " + index + ".");
182:
183: // first, mark the instruction
184: MarkedInstruction mi = this .list.getInstructionAt(index);
185: mi.addMark(this .measureIndex, count);
186:
187: MarkRecord mr = new MarkRecord(meta, this .analysisModule
188: .getMeasureName(), getMethodName(), count,
189: getSourceLine(mi));
190: cr.addMark(mr);
191: }
192:
193: /**
194: * Output a friendly version of the object.
195: */
196: public String toString() {
197: return getClassName() + "#" + getMethodName();
198: }
199:
200: private int getSourceLine(MarkedInstruction mi) {
201: int ret = -1;
202: LineNumberTable lnt = getLineNumberTable();
203: if (lnt != null) {
204: // see bug 938439
205: // Fix by Stefano Turri
206: try {
207: ret = lnt.getSourceLine(mi.getInstructionPosition());
208: } catch (ArrayIndexOutOfBoundsException e) {
209: ret = -1;
210: }
211: }
212: return ret;
213: }
214: }
|