<commit_before>/*
 * 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;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.bcel.Constants;
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.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;

import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.ba.ca.CallListAnalysis;
import edu.umd.cs.findbugs.ba.ca.CallListDataflow;
import edu.umd.cs.findbugs.ba.constant.ConstantAnalysis;
import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefAnalysis;
import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow;
import edu.umd.cs.findbugs.ba.heap.LoadAnalysis;
import edu.umd.cs.findbugs.ba.heap.LoadDataflow;
import edu.umd.cs.findbugs.ba.heap.StoreAnalysis;
import edu.umd.cs.findbugs.ba.heap.StoreDataflow;
import edu.umd.cs.findbugs.ba.npe.IsNullValueAnalysis;
import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
import edu.umd.cs.findbugs.ba.npe.UnconditionalDerefAnalysis;
import edu.umd.cs.findbugs.ba.npe.UnconditionalDerefDataflow;
import edu.umd.cs.findbugs.ba.type.ExceptionSetFactory;
import edu.umd.cs.findbugs.ba.type.TypeAnalysis;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.vna.LoadedFieldSet;
import edu.umd.cs.findbugs.ba.vna.MergeTree;
import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.util.MapCache;

/**
 * A ClassContext caches all of the auxiliary objects used to analyze
 * the methods of a class.  That way, these objects don't need to
 * be created over and over again.
 *
 * @author <NAME>
 */
public class ClassContext {
	public static final boolean DEBUG = SystemProperties.getBoolean("classContext.debug");

	private static final int PRUNED_INFEASIBLE_EXCEPTIONS = 1;
	private static final int PRUNED_UNCONDITIONAL_THROWERS = 2;

	private static final boolean TIME_ANALYSES = SystemProperties.getBoolean("classContext.timeAnalyses");

	private static final boolean DEBUG_CFG = SystemProperties.getBoolean("classContext.debugCFG");

	/* ----------------------------------------------------------------------
	 * Helper classes
	 * ---------------------------------------------------------------------- */
	
	/**
	 * Unpacked code for a method.
	 * Contains set of all opcodes in the method, as well as a map
	 * of bytecode offsets to opcodes.
	 */
	private static class UnpackedCode {
		private BitSet bytecodeSet;
		private short[] offsetToBytecodeMap;
		public UnpackedCode(BitSet bytecodeSet, short[] offsetToBytecodeMap) {
			this.bytecodeSet = bytecodeSet;
			this.offsetToBytecodeMap = offsetToBytecodeMap;
		}
		
		/**
		 * @return Returns the bytecodeSet.
		 */
		public BitSet getBytecodeSet() {
			return bytecodeSet;
		}
		
		/**
		 * @return Returns the offsetToBytecodeMap.
		 */
		public short[] getOffsetToBytecodeMap() {
			return offsetToBytecodeMap;
		}
	}

	private static int depth;

	private static void indent() {
		for (int i = 0; i < depth; ++i) System.out.print("  ");
	}
	
	/**
	 * An AnalysisResult stores the result of requesting an analysis
	 * from an AnalysisFactory.  It can represent a successful outcome
	 * (where the Analysis object can be returned), or an unsuccessful
	 * outcome (where an exception was thrown trying to create the
	 * analysis).  For unsuccessful outcomes, we rethrow the original
	 * exception rather than making another attempt to create the analysis
	 * (since if it fails once, it will never succeed). 
	 */
	private static class AnalysisResult<Analysis> {
		private boolean analysisSetExplicitly;
		private Analysis analysis;
		private AnalysisException analysisException;
		private CFGBuilderException cfgBuilderException;
		private DataflowAnalysisException dataflowAnalysisException;
		
		public Analysis getAnalysis() throws CFGBuilderException, DataflowAnalysisException {
			if (analysisSetExplicitly)
				return analysis;
			if (dataflowAnalysisException != null)
				throw dataflowAnalysisException;
			if (analysisException != null)
				throw analysisException;
			if (cfgBuilderException != null)
				throw cfgBuilderException;
			throw new IllegalStateException();
		}
		
		/**
		 * Record a successful outcome, where the analysis was created.
		 * 
		 * @param analysis the Analysis
		 */
		public void setAnalysis(@Nullable Analysis analysis) {
			this.analysisSetExplicitly = true;
			this.analysis = analysis;
		}
		
		/**
		 * Record that an AnalysisException occurred while attempting
		 * to create the Analysis.
		 * 
		 * @param analysisException the AnalysisException
		 */
		public void setAnalysisException(AnalysisException analysisException) {
			this.analysisException = analysisException;
		}
		
		/**
		 * Record that a CFGBuilderException occurred while attempting
		 * to create the Analysis.
		 * 
		 * @param cfgBuilderException the CFGBuilderException
		 */
		public void setCFGBuilderException(CFGBuilderException cfgBuilderException) {
			this.cfgBuilderException = cfgBuilderException;
		}
		
		/**
		 * Record that a DataflowAnalysisException occurred while attempting
		 * to create the Analysis.
		 *  
		 * @param dataflowException the DataflowAnalysisException
		 */
		public void setDataflowAnalysisException(DataflowAnalysisException dataflowException) {
			this.dataflowAnalysisException = dataflowException;
		}
	}

	/**
	 * Abstract factory class for creating analysis objects.
	 * Handles caching of analysis results for a method.
	 */
	private abstract class AnalysisFactory <Analysis> {
		private String analysisName;
		private HashMap<Method, AnalysisResult<Analysis>> map =
			new HashMap<Method, ClassContext.AnalysisResult<Analysis>>();

		/**
		 * Constructor.
		 * 
		 * @param analysisName name of the analysis factory: for diagnostics/debugging
		 */
		public AnalysisFactory(String analysisName) {
			this.analysisName = analysisName;
			
			analysisFactoryList.add(this);
		}

		/**
		 * Get the Analysis for given method.
		 * If Analysis has already been performed, the cached result is
		 * returned.
		 * 
		 * @param method the method to analyze
		 * @return the Analysis object representing the result of analyzing the method
		 * @throws CFGBuilderException       if the CFG can't be constructed for the method
		 * @throws DataflowAnalysisException if dataflow analysis fails on the method
		 */
		@CheckForNull public Analysis getAnalysis(Method method) throws CFGBuilderException, DataflowAnalysisException {
			AnalysisResult<Analysis> result = map.get(method);
			if (result == null) {
				if (TIME_ANALYSES) {
					++depth;
					indent();
					System.out.println("CC: Starting " + analysisName + " for " +
					        SignatureConverter.convertMethodSignature(jclass, method) + ":");
				}

				long begin = System.currentTimeMillis();
				
				// Create a new AnalysisResult
				result = new AnalysisResult<Analysis>();

				// Attempt to create the Analysis and store it in the AnalysisResult.
				// If an exception occurs, record it in the AnalysisResult.
				Analysis analysis;
				try {
					analysis = analyze(method);
					result.setAnalysis(analysis);
				} catch (CFGBuilderException e) {
					result.setCFGBuilderException(e);
				} catch (DataflowAnalysisException e) {
					result.setDataflowAnalysisException(e);
				} catch (AnalysisException e) {
					result.setAnalysisException(e);
				}

				if (TIME_ANALYSES) {
					long end = System.currentTimeMillis();
					indent();
					System.out.println("CC: finished " + analysisName + " in " + (end - begin) + " millis");
					--depth;
				}

				// Cache the outcome of this analysis attempt.
				map.put(method, result);
			}
			
			return result.getAnalysis();
		}

		@CheckForNull protected abstract Analysis analyze(Method method)
		        throws CFGBuilderException, DataflowAnalysisException;

		/**
		 * @return true if this analysis factory is a dataflow analysis,
		 *          false if not
		 */
		public abstract boolean isDataflow();

		/**
		 * Purge result for given method.
		 * 
		 * @param method the method whose analysis result should purged 
		 */
		public void purge(Method method) {
			map.remove(method);
		}
	}

	private abstract class NoExceptionAnalysisFactory <Analysis> extends AnalysisFactory<Analysis> {
		public NoExceptionAnalysisFactory(String analysisName) {
			super(analysisName);
		}

		@Override
		public Analysis getAnalysis(Method method) {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			} catch (CFGBuilderException e) {
				throw new IllegalStateException("Should not happen");
			}
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return false;
		}
	}

	private abstract class NoDataflowAnalysisFactory <Analysis> extends AnalysisFactory<Analysis> {
		public NoDataflowAnalysisFactory(String analysisName) {
			super(analysisName);
		}

		@Override
		public Analysis getAnalysis(Method method) throws CFGBuilderException {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			}
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return false;
		}
	}

	private final Set<String> busyCFGSet = new HashSet<String>();

	private class CFGFactory extends AnalysisFactory<CFG> {

		public CFGFactory() {
			super("CFG construction");
		}

		@Override
                 public CFG getAnalysis(Method method) throws CFGBuilderException {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			}
		}

		public CFG getRawCFG(Method method) throws CFGBuilderException {
			return getAnalysis(method);
		}

		public CFG getRefinedCFG(Method method) throws CFGBuilderException {
			MethodGen methodGen = getMethodGen(method);
			if (methodGen == null) {
				JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(jclass, method);
				getLookupFailureCallback().reportSkippedAnalysis(javaClassAndMethod.toMethodDescriptor());
				throw new MethodUnprofitableException(javaClassAndMethod);
			}
			CFG cfg = getRawCFG(method);
			
			// Record method name and signature for informational purposes
			cfg.setMethodName(SignatureConverter.convertMethodSignature(methodGen));

			// HACK:
			// Due to recursive method invocations, we may get a recursive
			// request for the pruned CFG of a method.  In this case,
			// we just return the raw CFG.
			String methodId = methodGen.getClassName() + "." + methodGen.getName() + ":" + methodGen.getSignature();
			if (DEBUG_CFG) {
				indent();
				System.out.println("CC: getting refined CFG for " + methodId);
			}
			if (DEBUG) System.out.println("ClassContext: request to prune " + methodId);
			if (!busyCFGSet.add(methodId))
				return cfg;

			final boolean PRUNE_INFEASIBLE_EXCEPTION_EDGES =
				analysisContext.getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS);
			
			boolean changed = false;
			
			if (PRUNE_INFEASIBLE_EXCEPTION_EDGES && !cfg.isFlagSet(PRUNED_INFEASIBLE_EXCEPTIONS)) {
				try {
					TypeDataflow typeDataflow = getTypeDataflow(method);
					// Exception edge pruning based on ExceptionSets.
					// Note: this is quite slow.
					PruneInfeasibleExceptionEdges pruner =
						new PruneInfeasibleExceptionEdges(cfg, methodGen, typeDataflow);
					pruner.execute();
					changed  = changed || pruner.wasCFGModified();
				} catch (DataflowAnalysisException e) {
					// FIXME: should report the error
				} catch (ClassNotFoundException e) {
					getLookupFailureCallback().reportMissingClass(e);
				}
			}
			cfg.setFlags(cfg.getFlags() | PRUNED_INFEASIBLE_EXCEPTIONS);
			
			final boolean PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES =
				!analysisContext.getBoolProperty(AnalysisFeatures.CONSERVE_SPACE);

			if (PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES && !cfg.isFlagSet(PRUNED_UNCONDITIONAL_THROWERS)) {
				try {
					PruneUnconditionalExceptionThrowerEdges pruner =
						new PruneUnconditionalExceptionThrowerEdges(methodGen, cfg, getConstantPoolGen(), analysisContext);
					pruner.execute();
					changed = changed || pruner.wasCFGModified();
				} catch (DataflowAnalysisException e) {
					// FIXME: should report the error
				}
			}
			cfg.setFlags(cfg.getFlags() | PRUNED_UNCONDITIONAL_THROWERS);
			
			if (changed) {
				ClassContext.this.purgeAnalysisResultsAfterCFGPruning(method);
			}

			busyCFGSet.remove(methodId);

			return cfg;
		}

		@Override
		protected CFG analyze(Method method) throws CFGBuilderException {
			MethodGen methodGen = getMethodGen(method);
			if (methodGen == null) {
				JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(jclass, method);
				getLookupFailureCallback().reportSkippedAnalysis(javaClassAndMethod.toMethodDescriptor());
				throw new MethodUnprofitableException(javaClassAndMethod);
			}
			CFGBuilder cfgBuilder = CFGBuilderFactory.create(methodGen);
			cfgBuilder.build();
			return cfgBuilder.getCFG();
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			// TODO Auto-generated method stub
			return false;
		}
	}
	
	private abstract class DataflowAnalysisFactory<Analysis>
		extends AnalysisFactory<Analysis> {
		
		DataflowAnalysisFactory(String desc) {
			super(desc);
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return true;
		}
	}<commit_msg>fixed comment<commit_after>/*
 * 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;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.bcel.Constants;
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.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;

import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.ba.ca.CallListAnalysis;
import edu.umd.cs.findbugs.ba.ca.CallListDataflow;
import edu.umd.cs.findbugs.ba.constant.ConstantAnalysis;
import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefAnalysis;
import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow;
import edu.umd.cs.findbugs.ba.heap.LoadAnalysis;
import edu.umd.cs.findbugs.ba.heap.LoadDataflow;
import edu.umd.cs.findbugs.ba.heap.StoreAnalysis;
import edu.umd.cs.findbugs.ba.heap.StoreDataflow;
import edu.umd.cs.findbugs.ba.npe.IsNullValueAnalysis;
import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
import edu.umd.cs.findbugs.ba.npe.UnconditionalDerefAnalysis;
import edu.umd.cs.findbugs.ba.npe.UnconditionalDerefDataflow;
import edu.umd.cs.findbugs.ba.type.ExceptionSetFactory;
import edu.umd.cs.findbugs.ba.type.TypeAnalysis;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.vna.LoadedFieldSet;
import edu.umd.cs.findbugs.ba.vna.MergeTree;
import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.util.MapCache;

/**
 * A ClassContext caches all of the auxiliary objects used to analyze
 * the methods of a class.  That way, these objects don't need to
 * be created over and over again.
 *
 * @author <NAME>
 */
public class ClassContext {
	public static final boolean DEBUG = SystemProperties.getBoolean("classContext.debug");

	private static final int PRUNED_INFEASIBLE_EXCEPTIONS = 1;
	private static final int PRUNED_UNCONDITIONAL_THROWERS = 2;

	private static final boolean TIME_ANALYSES = SystemProperties.getBoolean("classContext.timeAnalyses");

	private static final boolean DEBUG_CFG = SystemProperties.getBoolean("classContext.debugCFG");

	/* ----------------------------------------------------------------------
	 * Helper classes
	 * ---------------------------------------------------------------------- */
	
	/**
	 * Unpacked code for a method.
	 * Contains set of all opcodes in the method, as well as a map
	 * of bytecode offsets to opcodes.
	 */
	private static class UnpackedCode {
		private BitSet bytecodeSet;
		private short[] offsetToBytecodeMap;
		public UnpackedCode(BitSet bytecodeSet, short[] offsetToBytecodeMap) {
			this.bytecodeSet = bytecodeSet;
			this.offsetToBytecodeMap = offsetToBytecodeMap;
		}
		
		/**
		 * @return Returns the bytecodeSet.
		 */
		public BitSet getBytecodeSet() {
			return bytecodeSet;
		}
		
		/**
		 * @return Returns the offsetToBytecodeMap.
		 */
		public short[] getOffsetToBytecodeMap() {
			return offsetToBytecodeMap;
		}
	}

	private static int depth;

	private static void indent() {
		for (int i = 0; i < depth; ++i) System.out.print("  ");
	}
	
	/**
	 * An AnalysisResult stores the result of requesting an analysis
	 * from an AnalysisFactory.  It can represent a successful outcome
	 * (where the Analysis object can be returned), or an unsuccessful
	 * outcome (where an exception was thrown trying to create the
	 * analysis).  For unsuccessful outcomes, we rethrow the original
	 * exception rather than making another attempt to create the analysis
	 * (since if it fails once, it will never succeed). 
	 */
	private static class AnalysisResult<Analysis> {
		private boolean analysisSetExplicitly;
		private Analysis analysis;
		private AnalysisException analysisException;
		private CFGBuilderException cfgBuilderException;
		private DataflowAnalysisException dataflowAnalysisException;
		
		public Analysis getAnalysis() throws CFGBuilderException, DataflowAnalysisException {
			if (analysisSetExplicitly)
				return analysis;
			if (dataflowAnalysisException != null)
				throw dataflowAnalysisException;
			if (analysisException != null)
				throw analysisException;
			if (cfgBuilderException != null)
				throw cfgBuilderException;
			throw new IllegalStateException();
		}
		
		/**
		 * Record a successful outcome, where the analysis was created.
		 * 
		 * @param analysis the Analysis
		 */
		public void setAnalysis(@Nullable Analysis analysis) {
			this.analysisSetExplicitly = true;
			this.analysis = analysis;
		}
		
		/**
		 * Record that an AnalysisException occurred while attempting
		 * to create the Analysis.
		 * 
		 * @param analysisException the AnalysisException
		 */
		public void setAnalysisException(AnalysisException analysisException) {
			this.analysisException = analysisException;
		}
		
		/**
		 * Record that a CFGBuilderException occurred while attempting
		 * to create the Analysis.
		 * 
		 * @param cfgBuilderException the CFGBuilderException
		 */
		public void setCFGBuilderException(CFGBuilderException cfgBuilderException) {
			this.cfgBuilderException = cfgBuilderException;
		}
		
		/**
		 * Record that a DataflowAnalysisException occurred while attempting
		 * to create the Analysis.
		 *  
		 * @param dataflowException the DataflowAnalysisException
		 */
		public void setDataflowAnalysisException(DataflowAnalysisException dataflowException) {
			this.dataflowAnalysisException = dataflowException;
		}
	}

	/**
	 * Abstract factory class for creating analysis objects.
	 * Handles caching of analysis results for a method.
	 */
	private abstract class AnalysisFactory <Analysis> {
		private String analysisName;
		private HashMap<Method, AnalysisResult<Analysis>> map =
			new HashMap<Method, ClassContext.AnalysisResult<Analysis>>();

		/**
		 * Constructor.
		 * 
		 * @param analysisName name of the analysis factory: for diagnostics/debugging
		 */
		public AnalysisFactory(String analysisName) {
			this.analysisName = analysisName;
			
			analysisFactoryList.add(this);
		}

		/**
		 * Get the Analysis for given method.
		 * If Analysis has already been performed, the cached result is
		 * returned.
		 * 
		 * @param method the method to analyze
		 * @return the Analysis object representing the result of analyzing the method
		 * @throws CFGBuilderException       if the CFG can't be constructed for the method
		 * @throws DataflowAnalysisException if dataflow analysis fails on the method
		 */
		@CheckForNull public Analysis getAnalysis(Method method) throws CFGBuilderException, DataflowAnalysisException {
			AnalysisResult<Analysis> result = map.get(method);
			if (result == null) {
				if (TIME_ANALYSES) {
					++depth;
					indent();
					System.out.println("CC: Starting " + analysisName + " for " +
					        SignatureConverter.convertMethodSignature(jclass, method) + ":");
				}

				long begin = System.currentTimeMillis();
				
				// Create a new AnalysisResult
				result = new AnalysisResult<Analysis>();

				// Attempt to create the Analysis and store it in the AnalysisResult.
				// If an exception occurs, record it in the AnalysisResult.
				Analysis analysis;
				try {
					analysis = analyze(method);
					result.setAnalysis(analysis);
				} catch (CFGBuilderException e) {
					result.setCFGBuilderException(e);
				} catch (DataflowAnalysisException e) {
					result.setDataflowAnalysisException(e);
				} catch (AnalysisException e) {
					result.setAnalysisException(e);
				}

				if (TIME_ANALYSES) {
					long end = System.currentTimeMillis();
					indent();
					System.out.println("CC: finished " + analysisName + " in " + (end - begin) + " millis");
					--depth;
				}

				// Cache the outcome of this analysis attempt.
				map.put(method, result);
			}
			
			return result.getAnalysis();
		}

		@CheckForNull protected abstract Analysis analyze(Method method)
		        throws CFGBuilderException, DataflowAnalysisException;

		/**
		 * @return true if this analysis factory is a dataflow analysis,
		 *          false if not
		 */
		public abstract boolean isDataflow();

		/**
		 * Purge result for given method.
		 * 
		 * @param method the method whose analysis result should purged 
		 */
		public void purge(Method method) {
			map.remove(method);
		}
	}

	private abstract class NoExceptionAnalysisFactory <Analysis> extends AnalysisFactory<Analysis> {
		public NoExceptionAnalysisFactory(String analysisName) {
			super(analysisName);
		}

		@Override
		public Analysis getAnalysis(Method method) {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			} catch (CFGBuilderException e) {
				throw new IllegalStateException("Should not happen");
			}
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return false;
		}
	}

	private abstract class NoDataflowAnalysisFactory <Analysis> extends AnalysisFactory<Analysis> {
		public NoDataflowAnalysisFactory(String analysisName) {
			super(analysisName);
		}

		@Override
		public Analysis getAnalysis(Method method) throws CFGBuilderException {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			}
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return false;
		}
	}

	private final Set<String> busyCFGSet = new HashSet<String>();

	private class CFGFactory extends AnalysisFactory<CFG> {

		public CFGFactory() {
			super("CFG construction");
		}

		@Override
                 public CFG getAnalysis(Method method) throws CFGBuilderException {
			try {
				return super.getAnalysis(method);
			} catch (DataflowAnalysisException e) {
				throw new IllegalStateException("Should not happen");
			}
		}

		public CFG getRawCFG(Method method) throws CFGBuilderException {
			return getAnalysis(method);
		}

		public CFG getRefinedCFG(Method method) throws CFGBuilderException {
			MethodGen methodGen = getMethodGen(method);
			if (methodGen == null) {
				JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(jclass, method);
				getLookupFailureCallback().reportSkippedAnalysis(javaClassAndMethod.toMethodDescriptor());
				throw new MethodUnprofitableException(javaClassAndMethod);
			}
			CFG cfg = getRawCFG(method);
			
			// Record method name and signature for informational purposes
			cfg.setMethodName(SignatureConverter.convertMethodSignature(methodGen));

			// HACK:
			// Due to recursive method invocations, we may get a recursive
			// request for the pruned CFG of a method.  In this case,
			// we just return the raw CFG.
			String methodId = methodGen.getClassName() + "." + methodGen.getName() + ":" + methodGen.getSignature();
			if (DEBUG_CFG) {
				indent();
				System.out.println("CC: getting refined CFG for " + methodId);
			}
			if (DEBUG) System.out.println("ClassContext: request to prune " + methodId);
			if (!busyCFGSet.add(methodId))
				return cfg;

			final boolean PRUNE_INFEASIBLE_EXCEPTION_EDGES =
				analysisContext.getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS);
			
			boolean changed = false;
			
			if (PRUNE_INFEASIBLE_EXCEPTION_EDGES && !cfg.isFlagSet(PRUNED_INFEASIBLE_EXCEPTIONS)) {
				try {
					TypeDataflow typeDataflow = getTypeDataflow(method);
					// Exception edge pruning based on ExceptionSets.
					// Note: this is quite slow.
					PruneInfeasibleExceptionEdges pruner =
						new PruneInfeasibleExceptionEdges(cfg, methodGen, typeDataflow);
					pruner.execute();
					changed  = changed || pruner.wasCFGModified();
				} catch (DataflowAnalysisException e) {
					// FIXME: should report the error
				} catch (ClassNotFoundException e) {
					getLookupFailureCallback().reportMissingClass(e);
				}
			}
			cfg.setFlags(cfg.getFlags() | PRUNED_INFEASIBLE_EXCEPTIONS);
			
			final boolean PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES =
				!analysisContext.getBoolProperty(AnalysisFeatures.CONSERVE_SPACE);

			if (PRUNE_UNCONDITIONAL_EXCEPTION_THROWER_EDGES && !cfg.isFlagSet(PRUNED_UNCONDITIONAL_THROWERS)) {
				try {
					PruneUnconditionalExceptionThrowerEdges pruner =
						new PruneUnconditionalExceptionThrowerEdges(methodGen, cfg, getConstantPoolGen(), analysisContext);
					pruner.execute();
					changed = changed || pruner.wasCFGModified();
				} catch (DataflowAnalysisException e) {
					// FIXME: should report the error
				}
			}
			cfg.setFlags(cfg.getFlags() | PRUNED_UNCONDITIONAL_THROWERS);
			
			if (changed) {
				ClassContext.this.purgeAnalysisResultsAfterCFGPruning(method);
			}

			busyCFGSet.remove(methodId);

			return cfg;
		}

		@Override
		protected CFG analyze(Method method) throws CFGBuilderException {
			MethodGen methodGen = getMethodGen(method);
			if (methodGen == null) {
				JavaClassAndMethod javaClassAndMethod = new JavaClassAndMethod(jclass, method);
				getLookupFailureCallback().reportSkippedAnalysis(javaClassAndMethod.toMethodDescriptor());
				throw new MethodUnprofitableException(javaClassAndMethod);
			}
			CFGBuilder cfgBuilder = CFGBuilderFactory.create(methodGen);
			cfgBuilder.build();
			return cfgBuilder.getCFG();
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return false;
		}
	}
	
	private abstract class DataflowAnalysisFactory<Analysis>
		extends AnalysisFactory<Analysis> {
		
		DataflowAnalysisFactory(String desc) {
			super(desc);
		}
		
		/* (non-Javadoc)
		 * @see edu.umd.cs.findbugs.ba.ClassContext.AnalysisFactory#isDataflow()
		 */
		@Override
		public boolean isDataflow() {
			return true;
		}
	}
{'fraction_non_alphanumeric': 0.09825072886297376, 'fraction_numerical': 0.001749271137026239, 'mean_word_length': 3.844029499450808, 'pattern_counts': {'":': 4, '<': 41, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1460018', 'n_tokens_mistral': 10341, 'n_tokens_neox': 8769, 'n_words': 2311}