📄 sourcelineannotation.java
字号:
/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003-2005, 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;import java.io.IOException;import org.apache.bcel.classfile.Code;import org.apache.bcel.classfile.JavaClass;import org.apache.bcel.classfile.LineNumber;import org.apache.bcel.classfile.LineNumberTable;import org.apache.bcel.classfile.Method;import org.apache.bcel.generic.InstructionHandle;import org.apache.bcel.generic.MethodGen;import edu.umd.cs.findbugs.annotations.CheckForNull;import edu.umd.cs.findbugs.annotations.NonNull;import edu.umd.cs.findbugs.ba.AnalysisContext;import edu.umd.cs.findbugs.ba.ClassContext;import edu.umd.cs.findbugs.ba.Hierarchy;import edu.umd.cs.findbugs.ba.JavaClassAndMethod;import edu.umd.cs.findbugs.ba.Location;import edu.umd.cs.findbugs.ba.SourceInfoMap;import edu.umd.cs.findbugs.ba.XMethod;import edu.umd.cs.findbugs.visitclass.PreorderVisitor;import edu.umd.cs.findbugs.xml.XMLAttributeList;import edu.umd.cs.findbugs.xml.XMLOutput;/** * A BugAnnotation that records a range of source lines * in a class. * * @author David Hovemeyer * @see BugAnnotation */public class SourceLineAnnotation implements BugAnnotation { private static final long serialVersionUID = 1L; public static final String DEFAULT_ROLE = "SOURCE_LINE_DEFAULT"; public static final String DEFAULT_ROLE_UNKNOWN_LINE = "SOURCE_LINE_DEFAULT_UNKNOWN_LINE"; public static final String ROLE_ANOTHER_INSTANCE = "SOURCE_LINE_ANOTHER_INSTANCE"; /** * String returned if the source file is unknown. * This must match what BCEL uses when the source file is unknown. */ public static final String UNKNOWN_SOURCE_FILE = "<Unknown>"; private String description; final private String className; private String sourceFile; final private int startLine; final private int endLine; final private int startBytecode; final private int endBytecode; private boolean synthetic; /** * Constructor. * * @param className the class to which the line number(s) refer * @param sourceFile the name of the source file * @param startLine the first line (inclusive) * @param endLine the ending line (inclusive) * @param startBytecode the first bytecode offset (inclusive) * @param endBytecode the end bytecode offset (inclusive) */ public SourceLineAnnotation(@NonNull String className, @NonNull String sourceFile, int startLine, int endLine, int startBytecode, int endBytecode) { if (className == null) throw new IllegalArgumentException("class name is null"); if (sourceFile == null) throw new IllegalArgumentException("source file is null"); this.description = DEFAULT_ROLE; this.className = className; this.sourceFile = sourceFile; this.startLine = startLine; this.endLine = endLine; this.startBytecode = startBytecode; this.endBytecode = endBytecode; } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } /** * Factory method to create an unknown source line annotation. * * @param className the class name * @param sourceFile the source file name * @return the SourceLineAnnotation */ public static SourceLineAnnotation createUnknown(String className, String sourceFile) { return createUnknown(className, sourceFile, -1, -1); } /** * Factory method to create an unknown source line annotation. * This variant looks up the source filename automatically * based on the class using best effort. * * @param className the class name * @return the SourceLineAnnotation */ public static SourceLineAnnotation createUnknown(String className) { return createUnknown( className, AnalysisContext.currentAnalysisContext().lookupSourceFile(className), -1, -1); } /** * Factory method to create an unknown source line annotation. * This doesn't use the analysis context. * * @param className the class name * @return the SourceLineAnnotation */ public static SourceLineAnnotation createReallyUnknown(String className) { return createUnknown( className, SourceLineAnnotation.UNKNOWN_SOURCE_FILE, -1, -1); } /** * Factory method to create an unknown source line annotation. * This variant is used when bytecode offsets are known, * but not source lines. * * @param className the class name * @param sourceFile the source file name * @return the SourceLineAnnotation */ public static SourceLineAnnotation createUnknown(String className, String sourceFile, int startBytecode, int endBytecode) { SourceLineAnnotation result = new SourceLineAnnotation(className, sourceFile, -1, -1, startBytecode, endBytecode); // result.setDescription("SOURCE_LINE_UNKNOWN"); return result; } /** * Factory method for creating a source line annotation describing * an entire method. * * @param visitor a BetterVisitor which is visiting the method * @return the SourceLineAnnotation */ public static SourceLineAnnotation fromVisitedMethod(PreorderVisitor visitor) { SourceLineAnnotation sourceLines = getSourceAnnotationForMethod( visitor.getDottedClassName(), visitor.getMethodName(), visitor.getMethodSig()); return sourceLines; } /** * Factory method for creating a source line annotation describing an entire * method. * * @param methodGen * the method being visited * @return the SourceLineAnnotation, or null if we do not have line number * information for the method */ public static SourceLineAnnotation fromVisitedMethod(MethodGen methodGen, String sourceFile) { LineNumberTable lineNumberTable = methodGen.getLineNumberTable(methodGen.getConstantPool()); String className = methodGen.getClassName(); int codeSize = methodGen.getInstructionList().getLength(); if (lineNumberTable == null) return createUnknown(className, sourceFile, 0, codeSize - 1); return forEntireMethod(className, sourceFile, lineNumberTable, codeSize); } /** * Create a SourceLineAnnotation covering an entire method. * * @param className name of the class the method is in * @param sourceFile source file containing the method * @param lineNumberTable the method's LineNumberTable * @param codeSize size in bytes of the method's code * @return a SourceLineAnnotation covering the entire method */ public static SourceLineAnnotation forEntireMethod(String className, String sourceFile, LineNumberTable lineNumberTable, int codeSize) { LineNumber[] table = lineNumberTable.getLineNumberTable(); if (table != null && table.length > 0) { LineNumber first = table[0]; LineNumber last = table[table.length - 1]; return new SourceLineAnnotation(className, sourceFile, first.getLineNumber(), last.getLineNumber(), 0, codeSize - 1); } else { return createUnknown(className, sourceFile, 0, codeSize - 1); } } /** * Create a SourceLineAnnotation covering an entire method. * * @param javaClass JavaClass containing the method * @param method the method * @return a SourceLineAnnotation for the entire method */ public static SourceLineAnnotation forEntireMethod(JavaClass javaClass, @CheckForNull Method method) { String sourceFile = javaClass.getSourceFileName(); if (method == null) return createUnknown(javaClass.getClassName(), sourceFile); Code code = method.getCode(); LineNumberTable lineNumberTable = method.getLineNumberTable(); if (code == null || lineNumberTable == null) { return createUnknown(javaClass.getClassName(), sourceFile); } return forEntireMethod(javaClass.getClassName(), sourceFile, lineNumberTable, code.getLength()); } /** * Create a SourceLineAnnotation covering an entire method. * * @param javaClass JavaClass containing the method * @param xmethod the method * @return a SourceLineAnnotation for the entire method */ public static SourceLineAnnotation forEntireMethod(JavaClass javaClass, XMethod xmethod) { JavaClassAndMethod m = Hierarchy.findMethod(javaClass, xmethod.getName(), xmethod.getSignature()); if (m == null) { return createUnknown(javaClass.getClassName(), javaClass.getSourceFileName()); } else { return forEntireMethod(javaClass, m.getMethod()); } } /** * Factory method for creating a source line annotation describing the * source line number for the instruction being visited by given visitor. * * @param visitor a BetterVisitor which is visiting the method * @param pc the bytecode offset of the instruction in the method * @return the SourceLineAnnotation, or null if we do not have line number information * for the instruction */ public static SourceLineAnnotation fromVisitedInstruction(BytecodeScanningDetector visitor, int pc) { return fromVisitedInstructionRange(visitor.getClassContext(), visitor, pc, pc); } /** * Factory method for creating a source line annotation describing the * source line number for the instruction being visited by given visitor. * * @param classContext the ClassContext * @param visitor a BetterVisitor which is visiting the method * @param pc the bytecode offset of the instruction in the method * @return the SourceLineAnnotation, or null if we do not have line number information * for the instruction */ public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, PreorderVisitor visitor, int pc) { return fromVisitedInstructionRange(classContext, visitor, pc, pc); } /** * Create from Method and Location in a visited class. * * @param classContext ClassContext of visited class * @param method Method in visited class * @param loc Location in visited class * @return SourceLineAnnotation describing visited Location */ public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, Method method, Location loc) { return fromVisitedInstruction(classContext, method, loc.getHandle()); } /** * Create from Method and InstructionHandle in a visited class. * * @param classContext ClassContext of visited class * @param method Method in visited class * @param handle InstructionHandle in visited class * @return SourceLineAnnotation describing visited instruction */ public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, Method method, InstructionHandle handle) { return fromVisitedInstruction(classContext, method, handle.getPosition()); } /** * Create from Method and bytecode offset in a visited class. * * @param classContext ClassContext of visited class * @param method Method in visited class * @param pc bytecode offset in visited method * @return SourceLineAnnotation describing visited instruction */ public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, Method method, int pc) { LineNumberTable lineNumberTable = method.getCode().getLineNumberTable(); String className = classContext.getJavaClass().getClassName(); String sourceFile = classContext.getJavaClass().getSourceFileName(); if (lineNumberTable == null) return createUnknown(className, sourceFile, pc, pc); int startLine = lineNumberTable.getSourceLine(pc); return new SourceLineAnnotation(className, sourceFile, startLine, startLine, pc, pc).addInstructionContext(classContext, method); } /** * Factory method for creating a source line annotation describing the * source line numbers for a range of instructions in the method being * visited by the given visitor. * * @param visitor a BetterVisitor which is visiting the method * @param startPC the bytecode offset of the start instruction in the range * @param endPC the bytecode offset of the end instruction in the range * @return the SourceLineAnnotation, or null if we do not have line number information * for the instruction */ public static SourceLineAnnotation fromVisitedInstructionRange( BytecodeScanningDetector visitor, int startPC, int endPC) { LineNumberTable lineNumberTable = getLineNumberTable(visitor); String className = visitor.getDottedClassName(); String sourceFile = visitor.getSourceFile(); if (lineNumberTable == null) return createUnknown(className, sourceFile, startPC, endPC); int startLine = lineNumberTable.getSourceLine(startPC); int endLine = lineNumberTable.getSourceLine(endPC); return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC) .addInstructionContext(visitor.getClassContext(), visitor.getMethod()); } /** * Factory method for creating a source line annotation describing the * source line numbers for a range of instructions in the method being * visited by the given visitor. * * @param classContext the ClassContext * @param visitor a BetterVisitor which is visiting the method * @param startPC the bytecode offset of the start instruction in the range * @param endPC the bytecode offset of the end instruction in the range * @return the SourceLineAnnotation, or null if we do not have line number information * for the instruction */ public static SourceLineAnnotation fromVisitedInstructionRange( ClassContext classContext, PreorderVisitor visitor, int startPC, int endPC) { LineNumberTable lineNumberTable = getLineNumberTable(visitor); String className = visitor.getDottedClassName(); String sourceFile = visitor.getSourceFile(); if (lineNumberTable == null) return createUnknown(className, sourceFile, startPC, endPC); int startLine = lineNumberTable.getSourceLine(startPC); int endLine = lineNumberTable.getSourceLine(endPC); return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC) .addInstructionContext(classContext, visitor.getMethod()); } public static SourceLineAnnotation fromRawData(String className, String sourceFile, int startLine, int endLine, int startPC, int endPC) { if (startLine == -1) return createUnknown(className, sourceFile, startPC, endPC); return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC) .addInstructionContext(null, null); // throw exception if we ever start using this } /** * Factory method for creating a source line annotation describing the * source line number for the instruction being visited by given visitor. * * @param visitor a DismantleBytecode visitor which is visiting the method * @return the SourceLineAnnotation, or null if we do not have line number information * for the instruction */ public static SourceLineAnnotation fromVisitedInstruction(BytecodeScanningDetector visitor) { return fromVisitedInstruction(visitor.getClassContext(), visitor, visitor.getPC()); } /** * Factory method for creating a source line annotation describing the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -