/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2003-2007 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.jsr305; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InvokeInstruction; import edu.umd.cs.findbugs.SystemProperties; import edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis; import edu.umd.cs.findbugs.ba.BasicBlock; import edu.umd.cs.findbugs.ba.CFG; import edu.umd.cs.findbugs.ba.DataflowAnalysisException; import edu.umd.cs.findbugs.ba.Edge; import edu.umd.cs.findbugs.ba.Location; import edu.umd.cs.findbugs.ba.XFactory; import edu.umd.cs.findbugs.ba.XMethod; import edu.umd.cs.findbugs.ba.vna.ValueNumber; import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow; import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame; import edu.umd.cs.findbugs.util.ClassName; /** * Abstract base class for type qualifier dataflow analyses. * * @author David Hovemeyer */ public abstract class TypeQualifierDataflowAnalysis extends AbstractDataflowAnalysis<TypeQualifierValueSet> { static String primitiveType (String simpleClass) { if (simpleClass.equals("Integer")) return "int"; return simpleClass.toLowerCase(); } static boolean isIdentifyFunctionForTypeQualifiers(XMethod m) { String className = m.getClassName(); if (!className.startsWith("java.lang")) return false; String methodName = m.getName(); if (m.isStatic()) { if (!methodName.equals("valueOf")) return false; String signature = m.getSignature(); if (signature.charAt(2) != ')') return false; } else { String simpleClassName = ClassName.extractSimpleName(className); if (!methodName.equals(primitiveType(simpleClassName) + "Value")) return false; String signature = m.getSignature(); if (signature.charAt(1) != ')') return false; } return true; } static final boolean DEBUG_VERBOSE = SystemProperties.getBoolean("ctq.dataflow.debug.verbose"); protected final XMethod xmethod; protected final CFG cfg; protected final ValueNumberDataflow vnaDataflow; protected final TypeQualifierValue typeQualifierValue; protected final ConstantPoolGen cpg; private final Map<Location, Set<SourceSinkInfo>> sourceSinkMap; /** * Constructor. * * @param xmethod * XMethod object containing information about the method being * analyzed * @param cfg * the control-flow graph (CFG) of the method being analyzed * @param vnaDataflow * ValueNumberDataflow for the method * @param cpg * the constant pool being built up * @param typeQualifierValue * the TypeQualifierValue we want the dataflow analysis to check */ public TypeQualifierDataflowAnalysis(XMethod xmethod, CFG cfg, ValueNumberDataflow vnaDataflow, ConstantPoolGen cpg, TypeQualifierValue typeQualifierValue) { this.xmethod = xmethod; this.cfg = cfg; this.vnaDataflow = vnaDataflow; this.cpg = cpg; this.typeQualifierValue = typeQualifierValue; this.sourceSinkMap = new HashMap<Location, Set<SourceSinkInfo>>(); } /* * (non-Javadoc) * * @see * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(java.lang.Object) */ public void initEntryFact(TypeQualifierValueSet result) throws DataflowAnalysisException { result.makeValid(); } /* * (non-Javadoc) * * @see * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#isFactValid(java.lang * .Object) */ @Override public boolean isFactValid(TypeQualifierValueSet fact) { return fact.isValid(); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(java.lang.Object, * java.lang.Object) */ public void copy(TypeQualifierValueSet source, TypeQualifierValueSet dest) { dest.makeSameAs(source); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#createFact() */ public TypeQualifierValueSet createFact() { return new TypeQualifierValueSet(typeQualifierValue); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#isTop(java.lang.Object) */ public boolean isTop(TypeQualifierValueSet fact) { return fact.isTop(); } /* * (non-Javadoc) * * @see * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(java.lang.Object) */ public void makeFactTop(TypeQualifierValueSet fact) { fact.setTop(); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object, * edu.umd.cs.findbugs.ba.Edge, java.lang.Object) */ public void meetInto(TypeQualifierValueSet fact, Edge edge, TypeQualifierValueSet result) throws DataflowAnalysisException { if (fact.isTop() || result.isBottom()) { // result does not change return; } else if (fact.isBottom() || result.isTop()) { result.makeSameAs(fact); return; } assert fact.isValid(); assert result.isValid(); result.mergeWith(fact); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(java.lang.Object, * java.lang.Object) */ public boolean same(TypeQualifierValueSet fact1, TypeQualifierValueSet fact2) { return fact1.equals(fact2); } /* * (non-Javadoc) * * @see * edu.umd.cs.findbugs.ba.BasicAbstractDataflowAnalysis#edgeTransfer(edu * .umd.cs.findbugs.ba.Edge, java.lang.Object) */ @Override public void edgeTransfer(Edge edge, TypeQualifierValueSet fact) throws DataflowAnalysisException { if (!fact.isValid()) { return; } // Propagate flow values and source information across phi nodes. ValueNumberFrame targetVnaFrame = vnaDataflow.getStartFact(edge.getTarget()); ValueNumberFrame sourceVnaFrame = vnaDataflow.getResultFact(edge.getSource()); if (!targetVnaFrame.isValid() || !sourceVnaFrame.isValid()) { return; } // The source and target frames can have different numbers of slots // if the target is an exception handler. // So, merge the minimum number of slots in either frame. int numSlotsToMerge = Math.min(sourceVnaFrame.getNumSlots(), targetVnaFrame.getNumSlots()); for (int i = 0; i < numSlotsToMerge; i++) { ValueNumber targetVN = targetVnaFrame.getValue(i); ValueNumber sourceVN = sourceVnaFrame.getValue(i); if (!targetVN.equals(sourceVN) && targetVN.hasFlag(ValueNumber.PHI_NODE)) { // targetVN is a phi result if (DEBUG_VERBOSE) { System.out.println("Phi node: " + fact.valueNumberToString(sourceVN) + " -> " + fact.valueNumberToString(targetVN)); } propagateAcrossPhiNode(fact, sourceVN, targetVN); if (DEBUG_VERBOSE) { String dir = isForwards() ? "forwards" : "backwards"; System.out.println("After propagating phi node " + dir + ": " + fact.toString()); } } } } protected abstract void propagateAcrossPhiNode(TypeQualifierValueSet fact, ValueNumber sourceVN, ValueNumber targetVN); /** * This method must be called before the dataflow analysis is executed. * * @throws DataflowAnalysisException */ public abstract void registerSourceSinkLocations() throws DataflowAnalysisException; protected void registerSourceSink(SourceSinkInfo sourceSinkInfo) { Set<SourceSinkInfo> set = sourceSinkMap.get(sourceSinkInfo.getLocation()); if (set == null) { set = new HashSet<SourceSinkInfo>(); sourceSinkMap.put(sourceSinkInfo.getLocation(), set); } set.add(sourceSinkInfo); } /** * Get the set of SourceSinkInfo objects representing sources/sinks at a * given Location. * * @param location * a Location * @return Set of SourceSinkInfo objects representing sources/sinks at the * Location */ public Set<SourceSinkInfo> getSourceSinkInfoSet(Location location) { Set<SourceSinkInfo> result = sourceSinkMap.get(location); return result != null ? result : Collections.<SourceSinkInfo> emptySet(); } /* * (non-Javadoc) * * @see * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transferInstruction(org * .apache.bcel.generic.InstructionHandle, * edu.umd.cs.findbugs.ba.BasicBlock, java.lang.Object) */ @Override public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, TypeQualifierValueSet fact) throws DataflowAnalysisException { if (!fact.isValid()) { return; } Location location = new Location(handle, basicBlock); Instruction i = handle.getInstruction(); if (i instanceof InvokeInstruction) { InvokeInstruction ii = (InvokeInstruction) i; XMethod m = XFactory.createXMethod(ii, cpg); if (TypeQualifierDataflowAnalysis.isIdentifyFunctionForTypeQualifiers(m)) { ValueNumberFrame vnaFrameAtLocation = vnaDataflow.getFactAtLocation(location); ValueNumberFrame vnaFrameAfterInstruction = vnaDataflow.getFactAfterLocation(location); ValueNumber in = vnaFrameAtLocation.getStackValue(0); ValueNumber out = vnaFrameAfterInstruction.getStackValue(0); fact.copyInfo(in, out); return; } } // This is a simple process. // Check to see if there are any sources/sinks at this location, // and if so, model them. Set<SourceSinkInfo> sourceSinkSet = sourceSinkMap.get(location); if (sourceSinkSet != null) { if (DEBUG_VERBOSE) { System.out.println("Modeling source/sink at " + location.toCompactString() + ": " + sourceSinkSet.toString()); } for (SourceSinkInfo sourceSinkInfo : sourceSinkSet) { fact.modelSourceSink(sourceSinkInfo); } } } }