/* * 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 java.util.BitSet; import java.util.Iterator; import javax.annotation.CheckForNull; import org.apache.bcel.classfile.LineNumberTable; import org.apache.bcel.classfile.LocalVariable; import org.apache.bcel.classfile.LocalVariableTable; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.IndexedInstruction; import org.apache.bcel.generic.InstructionHandle; import edu.umd.cs.findbugs.OpcodeStack.Item; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.CFG; import edu.umd.cs.findbugs.ba.CFGBuilderException; import edu.umd.cs.findbugs.ba.ClassContext; import edu.umd.cs.findbugs.ba.Dataflow; import edu.umd.cs.findbugs.ba.DataflowAnalysisException; import edu.umd.cs.findbugs.ba.LiveLocalStoreAnalysis; import edu.umd.cs.findbugs.ba.Location; import edu.umd.cs.findbugs.ba.SignatureParser; import edu.umd.cs.findbugs.util.EditDistance; import edu.umd.cs.findbugs.visitclass.DismantleBytecode; import edu.umd.cs.findbugs.visitclass.PreorderVisitor; import edu.umd.cs.findbugs.xml.XMLAttributeList; import edu.umd.cs.findbugs.xml.XMLOutput; /** * Bug annotation class for local variable names * * @author William Pugh * @see BugAnnotation */ public class LocalVariableAnnotation implements BugAnnotation { private static final long serialVersionUID = 1L; public static final String DEFAULT_ROLE = "LOCAL_VARIABLE_DEFAULT"; public static final String NAMED_ROLE = "LOCAL_VARIABLE_NAMED"; public static final String UNKNOWN_ROLE = "LOCAL_VARIABLE_UNKNOWN"; public static final String PARAMETER_ROLE = "LOCAL_VARIABLE_PARAMETER"; public static final String PARAMETER_NAMED_ROLE = "LOCAL_VARIABLE_PARAMETER_NAMED"; public static final String PARAMETER_VALUE_SOURCE_ROLE = "LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE"; public static final String PARAMETER_VALUE_SOURCE_NAMED_ROLE = "LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE_NAMED"; public static final String VALUE_DOOMED_ROLE = "LOCAL_VARIABLE_VALUE_DOOMED"; public static final String VALUE_DOOMED_NAMED_ROLE = "LOCAL_VARIABLE_VALUE_DOOMED_NAMED"; public static final String DID_YOU_MEAN_ROLE = "LOCAL_VARIABLE_DID_YOU_MEAN"; public static final String INVOKED_ON_ROLE = "LOCAL_VARIABLE_INVOKED_ON"; public static final String ARGUMENT_ROLE = "LOCAL_VARIABLE_ARGUMENT"; public static final String VALUE_OF_ROLE = "LOCAL_VARIABLE_VALUE_OF"; final private String name; final int register, pc; final int line; private String description; /** * Constructor. * * @param name * the name of the local variable * @param register * the local variable index * @param pc * the bytecode offset of the instruction that mentions this * local variable */ public LocalVariableAnnotation(String name, int register, int pc) { this.name = name; this.register = register; this.pc = pc; this.line = -1; this.description = DEFAULT_ROLE; this.setDescription(name.equals("?") ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED"); } /** * Constructor. * * @param name * the name of the local variable * @param register * the local variable index * @param pc * the bytecode offset of the instruction that mentions this * local variable */ public LocalVariableAnnotation(String name, int register, int pc, int line) { this.name = name; this.register = register; this.pc = pc; this.line = line; this.description = DEFAULT_ROLE; this.setDescription(name.equals("?") ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED"); } public static LocalVariableAnnotation getLocalVariableAnnotation(Method method, Location location, IndexedInstruction ins) { int local = ins.getIndex(); InstructionHandle handle = location.getHandle(); int position1 = handle.getNext().getPosition(); int position2 = handle.getPosition(); return getLocalVariableAnnotation(method, local, position1, position2); } public static LocalVariableAnnotation getLocalVariableAnnotation(Method method, int local, int position1, int position2) { LocalVariableTable localVariableTable = method.getLocalVariableTable(); String localName = "?"; if (localVariableTable != null) { LocalVariable lv1 = localVariableTable.getLocalVariable(local, position1); if (lv1 == null) { lv1 = localVariableTable.getLocalVariable(local, position2); position1 = position2; } if (lv1 != null) localName = lv1.getName(); else for (LocalVariable lv : localVariableTable.getLocalVariableTable()) { if (lv.getIndex() == local) { if (!localName.equals("?") && !localName.equals(lv.getName())) { // not a single consistent name localName = "?"; break; } localName = lv.getName(); } } } LineNumberTable lineNumbers = method.getLineNumberTable(); if (lineNumbers == null) return new LocalVariableAnnotation(localName, local, position1); int line = lineNumbers.getSourceLine(position1); return new LocalVariableAnnotation(localName, local, position1, line); } /** * Get a local variable annotation describing a parameter. * * @param method * a Method * @param local * the local variable containing the parameter * @return LocalVariableAnnotation describing the parameter */ public static LocalVariableAnnotation getParameterLocalVariableAnnotation(Method method, int local) { LocalVariableAnnotation lva = getLocalVariableAnnotation(method, local, 0, 0); if (lva.isNamed()) lva.setDescription(LocalVariableAnnotation.PARAMETER_NAMED_ROLE); else lva.setDescription(LocalVariableAnnotation.PARAMETER_ROLE); return lva; } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } public void accept(BugAnnotationVisitor visitor) { visitor.visitLocalVariableAnnotation(this); } public String format(String key, ClassAnnotation primaryClass) { // System.out.println("format: " + key + " reg: " + register + " name: " // + value); if (key.equals("hash")) { if (register < 0) return "??"; return name; } if (register < 0) return "?"; if (key.equals("register")) return String.valueOf(register); else if (key.equals("pc")) return String.valueOf(pc); else if (key.equals("name") || key.equals("givenClass")) return name; else if (!name.equals("?")) return name; return "$L" + register; } public void setDescription(String description) { this.description = description.intern(); } public String getDescription() { return description; } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object o) { if (!(o instanceof LocalVariableAnnotation)) return false; return name.equals(((LocalVariableAnnotation) o).name); } public int compareTo(BugAnnotation o) { if (!(o instanceof LocalVariableAnnotation)) // BugAnnotations must be // Comparable with any type // of BugAnnotation return this.getClass().getName().compareTo(o.getClass().getName()); return name.compareTo(((LocalVariableAnnotation) o).name); } @Override public String toString() { String pattern = I18N.instance().getAnnotationDescription(description); FindBugsMessageFormat format = new FindBugsMessageFormat(pattern); return format.format(new BugAnnotation[] { this }, null); } /* * ---------------------------------------------------------------------- * XML Conversion support * ---------------------------------------------------------------------- */ private static final String ELEMENT_NAME = "LocalVariable"; public void writeXML(XMLOutput xmlOutput) throws IOException { writeXML(xmlOutput, false, false); } public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException { XMLAttributeList attributeList = new XMLAttributeList().addAttribute("name", name) .addAttribute("register", String.valueOf(register)).addAttribute("pc", String.valueOf(pc)); String role = getDescription(); if (!role.equals(DEFAULT_ROLE)) attributeList.addAttribute("role", role); BugAnnotationUtil.writeXML(xmlOutput, ELEMENT_NAME, this, attributeList, addMessages); } public boolean isNamed() { return register >= 0 && !name.equals("?"); } /** * @return name of local variable */ public String getName() { return name; } public int getPC() { return pc; } public int getRegister() { return register; } public boolean isSignificant() { return !name.equals("?"); } /** * @param method * @param item * @param pc2 * @return */ public static @CheckForNull LocalVariableAnnotation getLocalVariableAnnotation(Method method, Item item, int pc) { int reg = item.getRegisterNumber(); if (reg < 0) return null; return getLocalVariableAnnotation(method, reg, pc, item.getPC()); } public static @CheckForNull LocalVariableAnnotation getLocalVariableAnnotation(DismantleBytecode visitor, Item item) { int reg = item.getRegisterNumber(); if (reg < 0) return null; return getLocalVariableAnnotation(visitor.getMethod(), reg, visitor.getPC(), item.getPC()); } public static @CheckForNull LocalVariableAnnotation findMatchingIgnoredParameter(ClassContext classContext, Method method, String name, String signature) { try { Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow = classContext.getLiveLocalStoreDataflow(method); CFG cfg; cfg = classContext.getCFG(method); LocalVariableAnnotation match = null; int lowestCost = Integer.MAX_VALUE; BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry()); int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature()); int startIndex = 0; if (!method.isStatic()) startIndex = 1; SignatureParser parser = new SignatureParser(method.getSignature()); Iterator<String> signatureIterator = parser.parameterSignatureIterator(); for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) { String sig = signatureIterator.next(); if (!liveStoreSetAtEntry.get(i) && signature.equals(sig)) { // parameter isn't live and signatures match LocalVariableAnnotation potentialMatch = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0); potentialMatch.setDescription(DID_YOU_MEAN_ROLE); if (!potentialMatch.isNamed()) return potentialMatch; int distance = EditDistance.editDistance(name, potentialMatch.getName()); if (distance < lowestCost) { match = potentialMatch; match.setDescription(DID_YOU_MEAN_ROLE); lowestCost = distance; } else if (distance == lowestCost) { // not unique best match match = null; } } } return match; } catch (DataflowAnalysisException e) { AnalysisContext.logError("", e); } catch (CFGBuilderException e) { AnalysisContext.logError("", e); } return null; } public static @CheckForNull LocalVariableAnnotation findUniqueBestMatchingParameter(ClassContext classContext, Method method, String name, String signature) { LocalVariableAnnotation match = null; int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature()); int startIndex = 0; if (!method.isStatic()) startIndex = 1; SignatureParser parser = new SignatureParser(method.getSignature()); Iterator<String> signatureIterator = parser.parameterSignatureIterator(); int lowestCost = Integer.MAX_VALUE; for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) { String sig = signatureIterator.next(); if (signature.equals(sig)) { LocalVariableAnnotation potentialMatch = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0); if (!potentialMatch.isNamed()) continue; int distance = EditDistance.editDistance(name, potentialMatch.getName()); if (distance < lowestCost) { match = potentialMatch; match.setDescription(DID_YOU_MEAN_ROLE); lowestCost = distance; } else if (distance == lowestCost) { // not unique best match match = null; } // signatures match } } if (lowestCost < 5) return match; return null; } public String toString(ClassAnnotation primaryClass) { return toString(); } } // vim:ts=4