All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.ba.type.TypeAnalysis Maven / Gradle / Ivy

The newest version!
/*
 * Bytecode Analysis Framework
 * 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.ba.type;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.CheckForNull;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.LocalVariableTypeTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.CodeExceptionGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.ExceptionThrower;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.Analyze;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.AnalysisFeatures;
import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.Dataflow;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.EdgeTypes;
import edu.umd.cs.findbugs.ba.FrameDataflowAnalysis;
import edu.umd.cs.findbugs.ba.Hierarchy;
import edu.umd.cs.findbugs.ba.Hierarchy2;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.ObjectTypeFactory;
import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
import edu.umd.cs.findbugs.ba.SignatureConverter;
import edu.umd.cs.findbugs.ba.generic.GenericSignatureParser;
import edu.umd.cs.findbugs.ba.generic.GenericUtilities;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;

/**
 * A forward dataflow analysis to determine the types of all values in the Java
 * stack frame at all points in a Java method. The values include local
 * variables and values on the Java operand stack.
 * 

*

* As a side effect, the analysis computes the exception set throwable on each * exception edge in the CFG. This information can be used to prune infeasible * exception edges, and mark exception edges which propagate only implicit * exceptions. * * @author David Hovemeyer * @see Dataflow * @see edu.umd.cs.findbugs.ba.DataflowAnalysis * @see TypeFrame */ public class TypeAnalysis extends FrameDataflowAnalysis implements EdgeTypes { public static final boolean DEBUG = SystemProperties.getBoolean("ta.debug"); /** * Force computation of accurate exceptions. */ public static final boolean FORCE_ACCURATE_EXCEPTIONS = SystemProperties.getBoolean("ta.accurateExceptions"); /** * Repository of information about thrown exceptions computed for a basic * block and its outgoing exception edges. It contains a result TypeFrame, * which is used to detect when the exception information needs to be * recomputed for the block. */ private class CachedExceptionSet { private final TypeFrame result; private final ExceptionSet exceptionSet; private final Map edgeExceptionMap; public CachedExceptionSet(TypeFrame result, ExceptionSet exceptionSet) { this.result = result; this.exceptionSet = exceptionSet; this.edgeExceptionMap = new HashMap(); } public boolean isUpToDate(TypeFrame result) { return this.result.equals(result); } public ExceptionSet getExceptionSet() { return exceptionSet; } public void setEdgeExceptionSet(Edge edge, ExceptionSet exceptionSet) { edgeExceptionMap.put(edge, exceptionSet); } public ExceptionSet getEdgeExceptionSet(Edge edge) { ExceptionSet edgeExceptionSet = edgeExceptionMap.get(edge); if (edgeExceptionSet == null) { edgeExceptionSet = exceptionSetFactory.createExceptionSet(); edgeExceptionMap.put(edge, edgeExceptionSet); } return edgeExceptionSet; } } /** * Cached information about an instanceof check. */ static class InstanceOfCheck { final ValueNumber valueNumber; final Type type; InstanceOfCheck(ValueNumber valueNumber, Type type) { this.valueNumber = valueNumber; this.type = type; } /** * @return Returns the valueNumber. */ public ValueNumber getValueNumber() { return valueNumber; } /** * @return Returns the type. */ public Type getType() { return type; } } protected MethodGen methodGen; private final Method method; protected CFG cfg; private final TypeMerger typeMerger; private final TypeFrameModelingVisitor visitor; private final Map thrownExceptionSetMap; private final RepositoryLookupFailureCallback lookupFailureCallback; private final ExceptionSetFactory exceptionSetFactory; private ValueNumberDataflow valueNumberDataflow; private final Map instanceOfCheckMap; /** * Constructor. * * @param method * TODO * @param methodGen * the MethodGen whose CFG we'll be analyzing * @param cfg * the control flow graph * @param dfs * DepthFirstSearch of the method * @param typeMerger * object to merge types * @param visitor * a TypeFrameModelingVisitor to use to model the effect of * instructions * @param lookupFailureCallback * lookup failure callback * @param exceptionSetFactory * factory for creating ExceptionSet objects */ public TypeAnalysis(Method method, MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, TypeMerger typeMerger, TypeFrameModelingVisitor visitor, RepositoryLookupFailureCallback lookupFailureCallback, ExceptionSetFactory exceptionSetFactory) { super(dfs); this.method = method; Code code = method.getCode(); if (code == null) { throw new IllegalArgumentException(method.getName() + " has no code"); } for (Attribute a : code.getAttributes()) { if (a instanceof LocalVariableTypeTable) { visitor.setLocalTypeTable((LocalVariableTypeTable) a); } } this.methodGen = methodGen; this.cfg = cfg; this.typeMerger = typeMerger; this.visitor = visitor; this.thrownExceptionSetMap = new HashMap(); this.lookupFailureCallback = lookupFailureCallback; this.exceptionSetFactory = exceptionSetFactory; this.instanceOfCheckMap = new HashMap(); if (DEBUG) { System.out.println("\n\nAnalyzing " + methodGen); } } /** * Constructor. * * @param method * TODO * @param methodGen * the MethodGen whose CFG we'll be analyzing * @param cfg * the control flow graph * @param dfs * DepthFirstSearch of the method * @param typeMerger * object to merge types * @param lookupFailureCallback * lookup failure callback * @param exceptionSetFactory * factory for creating ExceptionSet objects */ public TypeAnalysis(Method method, MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, TypeMerger typeMerger, RepositoryLookupFailureCallback lookupFailureCallback, ExceptionSetFactory exceptionSetFactory) { this(method, methodGen, cfg, dfs, typeMerger, new TypeFrameModelingVisitor(methodGen.getConstantPool(), typeMerger), lookupFailureCallback, exceptionSetFactory); if (TypeFrameModelingVisitor.DEBUG) { System.out.println(methodGen.getClassName() + "." + methodGen.getName() + " " + methodGen.getSignature()); } } /** * Constructor which uses StandardTypeMerger. * * @param method * TODO * @param methodGen * the MethodGen whose CFG we'll be analyzing * @param cfg * the control flow graph * @param dfs * DepthFirstSearch of the method * @param lookupFailureCallback * callback for Repository lookup failures * @param exceptionSetFactory * factory for creating ExceptionSet objects */ public TypeAnalysis(Method method, MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, RepositoryLookupFailureCallback lookupFailureCallback, ExceptionSetFactory exceptionSetFactory) { this(method, methodGen, cfg, dfs, new StandardTypeMerger(lookupFailureCallback, exceptionSetFactory), lookupFailureCallback, exceptionSetFactory); } /** * Set the ValueNumberDataflow for the method being analyzed. This is * optional; if set, it will be used to make instanceof instructions more * precise. * * @param valueNumberDataflow * the ValueNumberDataflow */ public void setValueNumberDataflow(ValueNumberDataflow valueNumberDataflow) { this.valueNumberDataflow = valueNumberDataflow; this.visitor.setValueNumberDataflow(valueNumberDataflow); } /** * Set the FieldStoreTypeDatabase. This can be used to get more accurate * types for values loaded from fields. * * @param database * the FieldStoreTypeDatabase */ public void setFieldStoreTypeDatabase(FieldStoreTypeDatabase database) { visitor.setFieldStoreTypeDatabase(database); } /** * Get the set of exceptions that can be thrown on given edge. This should * only be called after the analysis completes. * * @param edge * the Edge * @return the ExceptionSet */ public ExceptionSet getEdgeExceptionSet(Edge edge) { CachedExceptionSet cachedExceptionSet = thrownExceptionSetMap.get(edge.getSource()); return cachedExceptionSet.getEdgeExceptionSet(edge); } @Override public TypeFrame createFact() { return new TypeFrame(methodGen.getMaxLocals()); } @Override public void initEntryFact(TypeFrame result) { // Make the frame valid result.setValid(); int slot = 0; // Clear the stack slots in the frame result.clearStack(); // Add local for "this" pointer, if present if (!methodGen.isStatic()) { result.setValue(slot++, ObjectTypeFactory.getInstance(methodGen.getClassName())); } // [Added: Support for Generics] // Get a parser that reads the generic signature of the method and // can be used to get the correct GenericObjectType if an argument // has a class type Iterator iter = GenericSignatureParser.getGenericSignatureIterator(method); // Add locals for parameters. // Note that long and double parameters need to be handled // specially because they occupy two locals. Type[] argumentTypes = methodGen.getArgumentTypes(); for (Type argType : argumentTypes) { // Add special "extra" type for long or double params. // These occupy the slot before the "plain" type. if (argType.getType() == Constants.T_LONG) { result.setValue(slot++, TypeFrame.getLongExtraType()); } else if (argType.getType() == Constants.T_DOUBLE) { result.setValue(slot++, TypeFrame.getDoubleExtraType()); } // [Added: Support for Generics] String s = (iter == null || !iter.hasNext()) ? null : iter.next(); if (s != null && (argType instanceof ObjectType || argType instanceof ArrayType) && !(argType instanceof ExceptionObjectType)) { // replace with a generic version of the type try { Type t = GenericUtilities.getType(s); if (t != null) { argType = t; } } catch (RuntimeException e) { } // degrade gracefully } // Add the plain parameter type. result.setValue(slot++, argType); } // Set remaining locals to BOTTOM; this will cause any // uses of them to be flagged while (slot < methodGen.getMaxLocals()) { result.setValue(slot++, TypeFrame.getBottomType()); } } @Override public void copy(TypeFrame source, TypeFrame dest) { dest.copyFrom(source); } @Override public void makeFactTop(TypeFrame fact) { fact.setTop(); } @Override public boolean isFactValid(TypeFrame fact) { return fact.isValid(); } @Override public boolean same(TypeFrame fact1, TypeFrame fact2) { return fact1.sameAs(fact2); } @Override public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, TypeFrame fact) throws DataflowAnalysisException { visitor.setFrameAndLocation(fact, new Location(handle, basicBlock)); visitor.analyzeInstruction(handle.getInstruction()); } @Override public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, TypeFrame start, TypeFrame result) throws DataflowAnalysisException { visitor.startBasicBlock(); super.transfer(basicBlock, end, start, result); // Compute thrown exception types computeThrownExceptionTypes(basicBlock, end, result); if (DEBUG) { System.out.println("After " + basicBlock.getFirstInstruction() + " -> " + basicBlock.getLastInstruction()); System.out.println(" frame: " + result); } // If this block ends with an instanceof check, // update the cached information about it. instanceOfCheckMap.remove(basicBlock); if (visitor.isInstanceOfFollowedByBranch()) { InstanceOfCheck check = new InstanceOfCheck(visitor.getInstanceOfValueNumber(), visitor.getInstanceOfType()); instanceOfCheckMap.put(basicBlock, check); } } private void computeThrownExceptionTypes(BasicBlock basicBlock, @CheckForNull InstructionHandle end, TypeFrame result) throws DataflowAnalysisException { // Do nothing if we're not computing propagated exceptions if (!(FORCE_ACCURATE_EXCEPTIONS || AnalysisContext.currentAnalysisContext().getBoolProperty( AnalysisFeatures.ACCURATE_EXCEPTIONS))) { return; } // Also, nothing to do if the block is not an exception thrower if (!basicBlock.isExceptionThrower()) { return; } // If cached results are up to date, don't recompute. CachedExceptionSet cachedExceptionSet = getCachedExceptionSet(basicBlock); if (cachedExceptionSet.isUpToDate(result)) { return; } // Figure out what exceptions can be thrown out // of the basic block, and mark each exception edge // with the set of exceptions which can be propagated // along the edge. int exceptionEdgeCount = 0; Edge lastExceptionEdge = null; for (Iterator i = cfg.outgoingEdgeIterator(basicBlock); i.hasNext();) { Edge e = i.next(); if (e.isExceptionEdge()) { exceptionEdgeCount++; lastExceptionEdge = e; } } if (exceptionEdgeCount == 0) { // System.out.println("Shouldn't all blocks have an exception edge"); return; } // Compute exceptions that can be thrown by the // basic block. cachedExceptionSet = computeBlockExceptionSet(basicBlock, result); if (exceptionEdgeCount == 1) { cachedExceptionSet.setEdgeExceptionSet(lastExceptionEdge, cachedExceptionSet.getExceptionSet()); return; } // For each outgoing exception edge, compute exceptions // that can be thrown. This assumes that the exception // edges are enumerated in decreasing order of priority. // In the process, this will remove exceptions from // the thrown exception set. ExceptionSet thrownExceptionSet = cachedExceptionSet.getExceptionSet(); if (!thrownExceptionSet.isEmpty()) { thrownExceptionSet = thrownExceptionSet.duplicate(); } for (Iterator i = cfg.outgoingEdgeIterator(basicBlock); i.hasNext();) { Edge edge = i.next(); if (edge.isExceptionEdge()) { cachedExceptionSet.setEdgeExceptionSet(edge, computeEdgeExceptionSet(edge, thrownExceptionSet)); } } } @Override public void meetInto(TypeFrame fact, Edge edge, TypeFrame result) throws DataflowAnalysisException { BasicBlock basicBlock = edge.getTarget(); if (fact.isValid()) { TypeFrame tmpFact = null; // Handling an exception? if (basicBlock.isExceptionHandler()) { tmpFact = modifyFrame(fact, null); // Special case: when merging predecessor facts for entry to // an exception handler, we clear the stack and push a // single entry for the exception object. That way, the locals // can still be merged. CodeExceptionGen exceptionGen = basicBlock.getExceptionGen(); tmpFact.clearStack(); // Determine the type of exception(s) caught. Type catchType = null; if (FORCE_ACCURATE_EXCEPTIONS || AnalysisContext.currentAnalysisContext().getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS)) { try { // Ideally, the exceptions that can be propagated // on this edge has already been computed. CachedExceptionSet cachedExceptionSet = getCachedExceptionSet(edge.getSource()); ExceptionSet edgeExceptionSet = cachedExceptionSet.getEdgeExceptionSet(edge); if (!edgeExceptionSet.isEmpty()) { // System.out.println("Using computed edge exception set!"); catchType = ExceptionObjectType.fromExceptionSet(edgeExceptionSet); } } catch (ClassNotFoundException e) { lookupFailureCallback.reportMissingClass(e); } } if (catchType == null) { // No information about propagated exceptions, so // pick a type conservatively using the handler catch type. catchType = exceptionGen.getCatchType(); if (catchType == null) { catchType = Type.THROWABLE; // handle catches anything // throwable } } tmpFact.pushValue(catchType); } // See if we can make some types more precise due to // a successful instanceof check in the source block. if (valueNumberDataflow != null) { tmpFact = handleInstanceOfBranch(fact, tmpFact, edge); } if (tmpFact != null) { fact = tmpFact; } } mergeInto(fact, result); } private TypeFrame handleInstanceOfBranch(TypeFrame fact, TypeFrame tmpFact, Edge edge) { InstanceOfCheck check = instanceOfCheckMap.get(edge.getSource()); if (check == null) { // System.out.println("no instanceof check for block " + // edge.getSource().getId()); return tmpFact; } if (check.getValueNumber() == null) { // System.out.println("instanceof check for block " + // edge.getSource().getId() + " has no value number"); return tmpFact; } ValueNumber instanceOfValueNumber = check.getValueNumber(); ValueNumberFrame vnaFrame = valueNumberDataflow.getStartFact(edge.getTarget()); if (!vnaFrame.isValid()) { return tmpFact; } Type instanceOfType = check.getType(); if (!(instanceOfType instanceof ReferenceType || instanceOfType instanceof NullType)) { return tmpFact; } short branchOpcode = edge.getSource().getLastInstruction().getInstruction().getOpcode(); int edgeType = edge.getType(); int numSlots = Math.min(fact.getNumSlots(), vnaFrame.getNumSlots()); if ((edgeType == EdgeTypes.IFCMP_EDGE && (branchOpcode == Constants.IFNE || branchOpcode == Constants.IFGT || branchOpcode == Constants.IFNULL)) || (edgeType == EdgeTypes.FALL_THROUGH_EDGE && (branchOpcode == Constants.IFEQ || branchOpcode == Constants.IFLE || branchOpcode == Constants.IFNONNULL))) { // System.out.println("Successful check on edge " + edge); // Successful instanceof check. for (int i = 0; i < numSlots; ++i) { if (!vnaFrame.getValue(i).equals(instanceOfValueNumber)) { continue; } Type checkedType = fact.getValue(i); if (!(checkedType instanceof ReferenceType)) { continue; } // Only refine the type if the cast is feasible: i.e., a // downcast. // Otherwise, just set it to TOP. try { boolean guaranteed = Hierarchy.isSubtype((ReferenceType) checkedType, (ReferenceType) instanceOfType); if (guaranteed) { continue; } boolean feasibleCheck = instanceOfType.equals(NullType.instance()) || Hierarchy.isSubtype((ReferenceType) instanceOfType, (ReferenceType) checkedType); if (!feasibleCheck && instanceOfType instanceof ObjectType && checkedType instanceof ObjectType) { double v = Analyze.deepInstanceOf(((ObjectType) instanceOfType).getClassName(), ((ObjectType) checkedType).getClassName()); if (v > 0.0) { feasibleCheck = true; } } tmpFact = modifyFrame(fact, tmpFact); if (feasibleCheck) { tmpFact.setValue(i, instanceOfType); } else { tmpFact.setTop(); return tmpFact; } } catch (ClassNotFoundException e) { lookupFailureCallback.reportMissingClass(e); return tmpFact; } } } else if (!instanceOfType.equals(NullType.instance())) { for (int i = 0; i < numSlots; ++i) { if (!vnaFrame.getValue(i).equals(instanceOfValueNumber)) { continue; } Type checkedType = fact.getValue(i); if (!(checkedType instanceof ReferenceType)) { continue; } try { boolean guaranteed = Hierarchy.isSubtype((ReferenceType) checkedType, (ReferenceType) instanceOfType); if (!guaranteed) { continue; } tmpFact = modifyFrame(fact, tmpFact); tmpFact.setTop(); return tmpFact; } catch (ClassNotFoundException e) { lookupFailureCallback.reportMissingClass(e); return tmpFact; } } } return tmpFact; } @Override protected void mergeValues(TypeFrame otherFrame, TypeFrame resultFrame, int slot) throws DataflowAnalysisException { Type type2 = resultFrame.getValue(slot); Type type1 = otherFrame.getValue(slot); Type value = typeMerger.mergeTypes(type2, type1); resultFrame.setValue(slot, value); // Result type is exact IFF types are identical and both are exact boolean typesAreIdentical = type1.equals(type2); boolean bothExact = resultFrame.isExact(slot) && otherFrame.isExact(slot); resultFrame.setExact(slot, typesAreIdentical && bothExact); } /** * Get the cached set of exceptions that can be thrown from given basic * block. If this information hasn't been computed yet, then an empty * exception set is returned. * * @param basicBlock * the block to get the cached exception set for * @return the CachedExceptionSet for the block */ private CachedExceptionSet getCachedExceptionSet(BasicBlock basicBlock) { CachedExceptionSet cachedExceptionSet = thrownExceptionSetMap.get(basicBlock); if (cachedExceptionSet == null) { // When creating the cached exception type set for the first time: // - the block result is set to TOP, so it won't match // any block result that has actually been computed // using the analysis transfer function // - the exception set is created as empty (which makes it // return TOP as its common superclass) TypeFrame top = createFact(); makeFactTop(top); cachedExceptionSet = new CachedExceptionSet(top, exceptionSetFactory.createExceptionSet()); thrownExceptionSetMap.put(basicBlock, cachedExceptionSet); } return cachedExceptionSet; } /** * Compute the set of exceptions that can be thrown from the given basic * block. This should only be called if the existing cached exception set is * out of date. * * @param basicBlock * the basic block * @param result * the result fact for the block; this is used to determine * whether or not the cached exception set is up to date * @return the cached exception set for the block */ private CachedExceptionSet computeBlockExceptionSet(BasicBlock basicBlock, TypeFrame result) throws DataflowAnalysisException { ExceptionSet exceptionSet = computeThrownExceptionTypes(basicBlock); TypeFrame copyOfResult = createFact(); copy(result, copyOfResult); CachedExceptionSet cachedExceptionSet = new CachedExceptionSet(copyOfResult, exceptionSet); thrownExceptionSetMap.put(basicBlock, cachedExceptionSet); return cachedExceptionSet; } /** * Based on the set of exceptions that can be thrown from the source basic * block, compute the set of exceptions that can propagate along given * exception edge. This method should be called for each outgoing exception * edge in sequence, so the caught exceptions can be removed from the thrown * exception set as needed. * * @param edge * the exception edge * @param thrownExceptionSet * current set of exceptions that can be thrown, taking earlier * (higher priority) exception edges into account * @return the set of exceptions that can propagate along this edge */ private ExceptionSet computeEdgeExceptionSet(Edge edge, ExceptionSet thrownExceptionSet) { if (thrownExceptionSet.isEmpty()) { return thrownExceptionSet; } ExceptionSet result = exceptionSetFactory.createExceptionSet(); if (edge.getType() == UNHANDLED_EXCEPTION_EDGE) { // The unhandled exception edge always comes // after all of the handled exception edges. result.addAll(thrownExceptionSet); thrownExceptionSet.clear(); return result; } BasicBlock handlerBlock = edge.getTarget(); CodeExceptionGen handler = handlerBlock.getExceptionGen(); ObjectType catchType = handler.getCatchType(); if (Hierarchy.isUniversalExceptionHandler(catchType)) { result.addAll(thrownExceptionSet); thrownExceptionSet.clear(); } else { // Go through the set of thrown exceptions. // Any that will DEFINITELY be caught be this handler, remove. // Any that MIGHT be caught, but won't definitely be caught, // remain. for (ExceptionSet.ThrownExceptionIterator i = thrownExceptionSet.iterator(); i.hasNext();) { // ThrownException thrownException = i.next(); ObjectType thrownType = i.next(); boolean explicit = i.isExplicit(); if (DEBUG) { System.out.println("\texception type " + thrownType + ", catch type " + catchType); } try { if (Hierarchy.isSubtype(thrownType, catchType)) { // Exception can be thrown along this edge result.add(thrownType, explicit); // And it will definitely be caught i.remove(); if (DEBUG) { System.out.println("\tException is subtype of catch type: " + "will definitely catch"); } } else if (Hierarchy.isSubtype(catchType, thrownType)) { // Exception possibly thrown along this edge result.add(thrownType, explicit); if (DEBUG) { System.out.println("\tException is supertype of catch type: " + "might catch"); } } } catch (ClassNotFoundException e) { // As a special case, if a class hierarchy lookup // fails, then we will conservatively assume that the // exception in question CAN, but WON'T NECESSARILY // be caught by the handler. AnalysisContext.reportMissingClass(e); result.add(thrownType, explicit); } } } return result; } /** * Compute the set of exception types that can be thrown by given basic * block. * * @param basicBlock * the basic block * @return the set of exceptions that can be thrown by the block */ private ExceptionSet computeThrownExceptionTypes(BasicBlock basicBlock) throws DataflowAnalysisException { ExceptionSet exceptionTypeSet = exceptionSetFactory.createExceptionSet(); InstructionHandle pei = basicBlock.getExceptionThrower(); Instruction ins = pei.getInstruction(); // Get the exceptions that BCEL knows about. // Note that all of these are unchecked. ExceptionThrower exceptionThrower = (ExceptionThrower) ins; Class[] exceptionList = exceptionThrower.getExceptions(); for (Class aExceptionList : exceptionList) { exceptionTypeSet.addImplicit(ObjectTypeFactory.getInstance(aExceptionList.getName())); } // Assume that an Error may be thrown by any instruction. exceptionTypeSet.addImplicit(Hierarchy.ERROR_TYPE); if (ins instanceof ATHROW) { // For ATHROW instructions, we generate *two* blocks // for which the ATHROW is an exception thrower. // // - The first, empty basic block, does the null check // - The second block, which actually contains the ATHROW, // throws the object on the top of the operand stack // // We make a special case of the block containing the ATHROW, // by removing all of the implicit exceptions, // and using type information to figure out what is thrown. if (basicBlock.containsInstruction(pei)) { // This is the actual ATHROW, not the null check // and implicit exceptions. exceptionTypeSet.clear(); // The frame containing the thrown value is the start fact // for the block, because ATHROW is guaranteed to be // the only instruction in the block. TypeFrame frame = getStartFact(basicBlock); // Check whether or not the frame is valid. // Sun's javac sometimes emits unreachable code. // For example, it will emit code that follows a JSR // subroutine call that never returns. // If the frame is invalid, then we can just make // a conservative assumption that anything could be // thrown at this ATHROW. if (!frame.isValid()) { exceptionTypeSet.addExplicit(Type.THROWABLE); } else if (frame.getStackDepth() == 0) { throw new IllegalStateException("empty stack " + " thrown by " + pei + " in " + SignatureConverter.convertMethodSignature(methodGen)); } else { Type throwType = frame.getTopValue(); if (throwType instanceof ObjectType) { exceptionTypeSet.addExplicit((ObjectType) throwType); } else if (throwType instanceof ExceptionObjectType) { exceptionTypeSet.addAll(((ExceptionObjectType) throwType).getExceptionSet()); } else { // Not sure what is being thrown here. // Be conservative. if (DEBUG) { System.out.println("Non object type " + throwType + " thrown by " + pei + " in " + SignatureConverter.convertMethodSignature(methodGen)); } exceptionTypeSet.addExplicit(Type.THROWABLE); } } } } // If it's an InvokeInstruction, add declared exceptions and // RuntimeException if (ins instanceof InvokeInstruction) { ConstantPoolGen cpg = methodGen.getConstantPool(); InvokeInstruction inv = (InvokeInstruction) ins; ObjectType[] declaredExceptionList = Hierarchy2.findDeclaredExceptions(inv, cpg); if (declaredExceptionList == null) { // Couldn't find declared exceptions, // so conservatively assume it could thrown any checked // exception. if (DEBUG) { System.out.println("Couldn't find declared exceptions for " + SignatureConverter.convertMethodSignature(inv, cpg)); } exceptionTypeSet.addExplicit(Hierarchy.EXCEPTION_TYPE); } else { for (ObjectType aDeclaredExceptionList : declaredExceptionList) { exceptionTypeSet.addExplicit(aDeclaredExceptionList); } } exceptionTypeSet.addImplicit(Hierarchy.RUNTIME_EXCEPTION_TYPE); } if (DEBUG) { System.out.println(pei + " can throw " + exceptionTypeSet); } return exceptionTypeSet; } @Override public String toString() { return this.getClass().getSimpleName() + "(" + methodGen.getClassName() + "." + methodGen.getMethod().getName() + methodGen.getMethod().getSignature() + ")"; } public boolean isImpliedByGenericTypes(ReferenceType t) { return visitor.isImpliedByGenericTypes(t); } // public static void main(String[] argv) throws Exception { // if (argv.length != 1) { // System.err.println("Usage: " + TypeAnalysis.class.getName() + // " "); // System.exit(1); // } // // DataflowTestDriver driver = new // DataflowTestDriver() { // @Override // public Dataflow createDataflow(ClassContext // classContext, Method method) // throws CFGBuilderException, DataflowAnalysisException { // return classContext.getTypeDataflow(method); // } // }; // // driver.execute(argv[0]); // } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy