001: /*
002: * @(#)BytecodeLineUtil.java
003: *
004: * Copyright (C) 2003-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.module;
028:
029: import java.util.Arrays;
030: import java.util.Comparator;
031:
032: import net.sourceforge.groboutils.codecoverage.v2.IAnalysisMetaData;
033: import net.sourceforge.groboutils.codecoverage.v2.IMethodCode;
034:
035: import org.apache.bcel.classfile.LineNumber;
036: import org.apache.bcel.classfile.LineNumberTable;
037: import org.apache.bcel.classfile.Method;
038: import org.apache.bcel.generic.InstructionHandle;
039: import org.apache.bcel.generic.InstructionList;
040:
041: /**
042: * Helper that processes bytecode instructions and line-numbering
043: *
044: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
045: * @author Juergen Kindler <a href="mailto:jkindler@freenet.de">jkindler@freenet.de</a>
046: * @version $Date: 2004/04/15 05:48:26 $
047: * @since February 18, 2003
048: * @see IAnalysisMetaData
049: */
050: public class BytecodeLineUtil {
051: private int[] instructionPos; // bytecode postion for instruction index
052: private int[] instructionLine; // linenumber for instruction index
053: private InstructionHandle[] handles;
054:
055: private static final LineNumberComparator LINENUMBER_COMPARATOR = new LineNumberComparator();
056:
057: /**
058: * Helper for sorting a line number table by program counter
059: *
060: * @author Juergen Kindler <a href="mailto:jkindler@freenet.de">jkindler@freenet.de</a>
061: */
062: private static class LineNumberComparator implements Comparator {
063: /**
064: * Compares the PC of two LineNumber objects.
065: * Other objects will lead to a ClassCastException.
066: *
067: * @param o1 an <code>Object</code> value
068: * @param o2 an <code>Object</code> value
069: * @return an <code>int</code> value
070: */
071: public int compare(Object o1, Object o2) {
072: return ((LineNumber) o1).getStartPC()
073: - ((LineNumber) o2).getStartPC();
074: }
075:
076: /**
077: * Compares equality of comparator objects.
078: * Returns that objects of same class as this one to be equal to this.
079: *
080: * @param o an <code>Object</code> value
081: * @return a <code>boolean</code> value
082: */
083: public boolean equals(Object o) {
084: return (o != null) ? (this .getClass() == o.getClass())
085: : false;
086: }
087: }
088:
089: /**
090: *
091: */
092: public BytecodeLineUtil(Method m) {
093: if (m == null) {
094: throw new IllegalArgumentException("no null args.");
095: }
096: initialize(m);
097: }
098:
099: public BytecodeLineUtil(IMethodCode m) {
100: if (m == null) {
101: throw new IllegalArgumentException("no null args.");
102: }
103: initialize(m.getOriginalMethod());
104: }
105:
106: public InstructionHandle[] getHandles() {
107: return this .handles;
108: }
109:
110: public int getLineNumber(InstructionHandle ih) {
111: if (ih == null) {
112: throw new IllegalArgumentException("no null args.");
113: }
114: return getLineNumberForBytecodePos(ih.getPosition());
115: }
116:
117: public int getLineNumberForBytecodePos(int bytecodePos) {
118: int instrPos = getInstructionPosForBytecodePos(bytecodePos);
119: return getLineNumberForInstructionPos(instrPos);
120: }
121:
122: public int getLineNumberForInstructionPos(int instrPos) {
123: if (instrPos >= 0 && instrPos < this .instructionLine.length) {
124: return this .instructionLine[instrPos];
125: }
126: //else
127: return -1;
128: }
129:
130: public int getInstructionPosForBytecodePos(int bytecodePos) {
131: // this method needs to account for the virtual instructions at the
132: // end of a method.
133:
134: int len = this .instructionPos.length;
135: if (len == 0) {
136: return 0;
137: }
138: for (int i = 0; i < len; ++i) {
139: if (this .instructionPos[i] == bytecodePos) {
140: return i;
141: }
142: }
143: if (len >= 1 && this .instructionPos[len - 1] < bytecodePos) {
144: return len;
145: }
146:
147: throw new IllegalStateException("Unknown bytecode position "
148: + bytecodePos);
149: }
150:
151: //-----------------------------------------------------------------------
152:
153: protected void initialize(Method m) {
154: if (m == null) {
155: throw new IllegalArgumentException("no null args.");
156: }
157: LineNumberTable lnt = m.getLineNumberTable();
158: LineNumber[] lines = sort(lnt);
159: this .instructionPos = getInstructionPositions(m);
160: this .instructionLine = getInstructionLines(lines,
161: this .instructionPos);
162: }
163:
164: private LineNumber[] sort(LineNumberTable lnt) {
165: if (lnt == null) {
166: return new LineNumber[0];
167: }
168: LineNumber[] lines = lnt.getLineNumberTable();
169: if (lines == null) {
170: return new LineNumber[0];
171: }
172:
173: Arrays.sort(lines, LINENUMBER_COMPARATOR);
174: return lines;
175: }
176:
177: private int[] getInstructionPositions(Method m) {
178: InstructionList il = new InstructionList(m.getCode().getCode());
179: il.setPositions();
180: this .handles = il.getInstructionHandles();
181:
182: int instructionCount = handles.length;
183: int instructionPos[] = new int[instructionCount];
184:
185: // find the positions of the instructions in the bytecode
186: for (int i = 0; i < instructionCount; ++i) {
187: instructionPos[i] = this .handles[i].getPosition();
188: }
189: return instructionPos;
190: }
191:
192: private int[] getInstructionLines(LineNumber lines[], int bytePos[]) {
193: int out[] = new int[bytePos.length];
194: for (int bIndex = 0; bIndex < bytePos.length; ++bIndex) {
195: out[bIndex] = getLinenoForBytePos(lines, bytePos[bIndex]);
196: }
197: return out;
198: }
199:
200: private int getLinenoForBytePos(LineNumber lines[], int bytePos) {
201: if (lines.length <= 0) {
202: return -1;
203: }
204:
205: for (int i = 1; i < lines.length; ++i) {
206: if (bytePos < lines[i].getStartPC()) {
207: return lines[i - 1].getLineNumber();
208: }
209: }
210: return lines[lines.length - 1].getLineNumber();
211: }
212: }
|