edu.umd.cs.findbugs.ba.LineNumberMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* Bytecode Analysis Framework
* Copyright (C) 2003,2004 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.ba;
import java.util.IdentityHashMap;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.SystemProperties;
/**
* Summarize line numbers (and other source information) for a method.
*/
public class LineNumberMap {
/**
* Set this property to true to get debug print statements.
*/
private static final boolean DEBUG = SystemProperties.getBoolean("lnm.debug");
/**
* When this is true, the workaround for the bug in BCEL 5.0's
* LineNumberTable class is disabled.
*/
private static final boolean LINE_NUMBER_BUG = SystemProperties.getBoolean("lineNumberBug");
private final MethodGen methodGen;
private final IdentityHashMap lineNumberMap;
private boolean hasLineNumbers;
/**
* Constructor.
*
* @param methodGen
* the method to summarize line numbers for
*/
public LineNumberMap(MethodGen methodGen) {
this.methodGen = methodGen;
lineNumberMap = new IdentityHashMap<>();
hasLineNumbers = false;
}
/**
* Build the line number information. Should be called before any other
* methods.
*/
public void build() {
int numGood = 0, numBytecodes = 0;
if (DEBUG) {
System.out.println("Method: " + methodGen.getName() + " - " + methodGen.getSignature() + "in class "
+ methodGen.getClassName());
}
// Associate line number information with each InstructionHandle
LineNumberTable table = methodGen.getLineNumberTable(methodGen.getConstantPool());
if (table != null && table.getTableLength() > 0) {
checkTable(table);
InstructionHandle handle = methodGen.getInstructionList().getStart();
while (handle != null) {
int bytecodeOffset = handle.getPosition();
if (bytecodeOffset < 0) {
throw new IllegalStateException("Bad bytecode offset: " + bytecodeOffset);
}
if (DEBUG) {
System.out.println("Looking for source line for bytecode offset " + bytecodeOffset);
}
int sourceLine;
try {
sourceLine = table.getSourceLine(bytecodeOffset);
} catch (ArrayIndexOutOfBoundsException e) {
if (LINE_NUMBER_BUG) {
throw e;
} else {
sourceLine = -1;
}
}
if (sourceLine >= 0) {
++numGood;
}
lineNumberMap.put(handle, new LineNumber(bytecodeOffset, sourceLine));
handle = handle.getNext();
++numBytecodes;
}
hasLineNumbers = true;
if (DEBUG) {
System.out.println("\t" + numGood + "/" + numBytecodes + " had valid line numbers");
}
}
}
private void checkTable(LineNumberTable table) {
if (DEBUG) {
System.out.println("line number table has length " + table.getTableLength());
}
LineNumber[] entries = table.getLineNumberTable();
int lastBytecode = -1;
for (int i = 0; i < entries.length; ++i) {
LineNumber ln = entries[i];
if (DEBUG) {
System.out.println("Entry " + i + ": pc=" + ln.getStartPC() + ", line=" + ln.getLineNumber());
}
int pc = ln.getStartPC();
if (pc <= lastBytecode) {
throw new IllegalStateException("LineNumberTable is not sorted");
}
}
}
/**
* Does this method have line number information?
*/
public boolean hasLineNumbers() {
return hasLineNumbers;
}
/**
* Find the line number information for instruction whose handle is given.
*
* @param handle
* the InstructionHandle
* @return the LineNumber object containing bytecode offset and source line
* number
*/
public LineNumber lookupLineNumber(InstructionHandle handle) {
return lineNumberMap.get(handle);
}
}