001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003,2004 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import java.util.IdentityHashMap;
023:
024: import org.apache.bcel.classfile.LineNumber;
025: import org.apache.bcel.classfile.LineNumberTable;
026: import org.apache.bcel.generic.InstructionHandle;
027: import org.apache.bcel.generic.MethodGen;
028:
029: import edu.umd.cs.findbugs.SystemProperties;
030:
031: /**
032: * Summarize line numbers (and other source information)
033: * for a method.
034: */
035: public class LineNumberMap {
036: /**
037: * Set this property to true to get debug print statements.
038: */
039: private static final boolean DEBUG = SystemProperties
040: .getBoolean("lnm.debug");
041:
042: /**
043: * When this is true, the workaround for the bug in BCEL 5.0's
044: * LineNumberTable class is disabled.
045: */
046: private static final boolean LINE_NUMBER_BUG = SystemProperties
047: .getBoolean("lineNumberBug");
048:
049: private MethodGen methodGen;
050: private IdentityHashMap<InstructionHandle, LineNumber> lineNumberMap;
051: private boolean hasLineNumbers;
052:
053: /**
054: * Constructor.
055: *
056: * @param methodGen the method to summarize line numbers for
057: */
058: public LineNumberMap(MethodGen methodGen) {
059: this .methodGen = methodGen;
060: lineNumberMap = new IdentityHashMap<InstructionHandle, LineNumber>();
061: hasLineNumbers = false;
062: }
063:
064: /**
065: * Build the line number information.
066: * Should be called before any other methods.
067: */
068: public void build() {
069: int numGood = 0, numBytecodes = 0;
070:
071: if (DEBUG) {
072: System.out.println("Method: " + methodGen.getName() + " - "
073: + methodGen.getSignature() + "in class "
074: + methodGen.getClassName());
075: }
076:
077: // Associate line number information with each InstructionHandle
078: LineNumberTable table = methodGen.getLineNumberTable(methodGen
079: .getConstantPool());
080:
081: if (table != null && table.getTableLength() > 0) {
082: checkTable(table);
083: InstructionHandle handle = methodGen.getInstructionList()
084: .getStart();
085: while (handle != null) {
086: int bytecodeOffset = handle.getPosition();
087: if (bytecodeOffset < 0)
088: throw new IllegalStateException(
089: "Bad bytecode offset: " + bytecodeOffset);
090: if (DEBUG)
091: System.out
092: .println("Looking for source line for bytecode offset "
093: + bytecodeOffset);
094: int sourceLine;
095: try {
096: sourceLine = table.getSourceLine(bytecodeOffset);
097: } catch (ArrayIndexOutOfBoundsException e) {
098: if (LINE_NUMBER_BUG)
099: throw e;
100: else
101: sourceLine = -1;
102: }
103: if (sourceLine >= 0)
104: ++numGood;
105: lineNumberMap.put(handle, new LineNumber(
106: bytecodeOffset, sourceLine));
107: handle = handle.getNext();
108: ++numBytecodes;
109: }
110: hasLineNumbers = true;
111:
112: if (DEBUG)
113: System.out.println("\t" + numGood + "/" + numBytecodes
114: + " had valid line numbers");
115: }
116: }
117:
118: private void checkTable(LineNumberTable table) {
119: if (DEBUG)
120: System.out.println("line number table has length "
121: + table.getTableLength());
122: LineNumber[] entries = table.getLineNumberTable();
123: int lastBytecode = -1;
124: for (int i = 0; i < entries.length; ++i) {
125: LineNumber ln = entries[i];
126: if (DEBUG)
127: System.out.println("Entry " + i + ": pc="
128: + ln.getStartPC() + ", line="
129: + ln.getLineNumber());
130: int pc = ln.getStartPC();
131: if (pc <= lastBytecode)
132: throw new IllegalStateException(
133: "LineNumberTable is not sorted");
134: }
135: }
136:
137: /**
138: * Does this method have line number information?
139: */
140: public boolean hasLineNumbers() {
141: return hasLineNumbers;
142: }
143:
144: /**
145: * Find the line number information for instruction whose
146: * handle is given.
147: *
148: * @param handle the InstructionHandle
149: * @return the LineNumber object containing bytecode offset and source line number
150: */
151: public LineNumber lookupLineNumber(InstructionHandle handle) {
152: return lineNumberMap.get(handle);
153: }
154: }
|