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

edu.umd.cs.findbugs.ba.bcp.PatternMatcher Maven / Gradle / Ivy

The newest version!
/*
 * Bytecode Analysis Framework
 * Copyright (C) 2003,2004 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.bcp;

import java.util.BitSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;

import javax.annotation.Nullable;

import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.BasicBlock;
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.DFSEdgeTypes;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.DominatorsAnalysis;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;

/**
 * 

Match a ByteCodePattern against the code of a method, represented by a CFG. * Produces some number of ByteCodePatternMatch objects, which indicate how the * pattern matched the bytecode instructions in the method. *

*

* This code is a hack and should probably be rewritten.

* * @author David Hovemeyer * @see ByteCodePattern */ public class PatternMatcher implements DFSEdgeTypes { private static final Logger LOG = LoggerFactory.getLogger(PatternMatcher.class); private static final boolean SHOW_WILD = SystemProperties.getBoolean("bcp.showWild"); private final ByteCodePattern pattern; private final CFG cfg; private final ConstantPoolGen cpg; private final DepthFirstSearch dfs; private final ValueNumberDataflow vnaDataflow; private final DominatorsAnalysis domAnalysis; private final LinkedList workList; private final IdentityHashMap visitedBlockMap; private final LinkedList resultList; /** * Constructor. * * @param pattern * the ByteCodePattern to look for examples of * @param classContext * ClassContext for the class to analyze * @param method * the Method to analyze */ public PatternMatcher(ByteCodePattern pattern, ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException { this.pattern = pattern; this.cfg = classContext.getCFG(method); this.cpg = classContext.getConstantPoolGen(); this.dfs = classContext.getDepthFirstSearch(method); this.vnaDataflow = classContext.getValueNumberDataflow(method); this.domAnalysis = classContext.getNonExceptionDominatorsAnalysis(method); this.workList = new LinkedList<>(); this.visitedBlockMap = new IdentityHashMap<>(); this.resultList = new LinkedList<>(); } /** * Search for examples of the ByteCodePattern. * * @return this object * @throws DataflowAnalysisException * if the ValueNumberAnalysis did not produce useful values for * the method */ public PatternMatcher execute() throws DataflowAnalysisException { workList.addLast(cfg.getEntry()); while (!workList.isEmpty()) { BasicBlock basicBlock = workList.removeLast(); visitedBlockMap.put(basicBlock, basicBlock); // Scan instructions of basic block for possible matches BasicBlock.InstructionIterator i = basicBlock.instructionIterator(); while (i.hasNext()) { attemptMatch(basicBlock, i.duplicate()); i.next(); } // Add successors of the basic block (which haven't been visited // already) Iterator succIterator = cfg.successorIterator(basicBlock); while (succIterator.hasNext()) { BasicBlock succ = succIterator.next(); if (visitedBlockMap.get(succ) == null) { workList.addLast(succ); } } } return this; } /** * Return an Iterator over the ByteCodePatternMatch objects representing * successful matches of the ByteCodePattern. */ public Iterator byteCodePatternMatchIterator() { return resultList.iterator(); } /** * Attempt to begin a match. * * @param basicBlock * the basic block * @param instructionIterator * the instruction iterator positioned just before the first * instruction to be matched */ private void attemptMatch(BasicBlock basicBlock, BasicBlock.InstructionIterator instructionIterator) throws DataflowAnalysisException { work(new State(basicBlock, instructionIterator, pattern.getFirst())); } // For debugging - number the paths through the CFG private int nextPath = 0; /** * Object representing the current state of the matching algorithm. Provides * convenient methods to implement the various steps of the algorithm. */ private class State { private final BasicBlock basicBlock; private final BasicBlock.InstructionIterator instructionIterator; private final PatternElement patternElement; private int matchCount; private PatternElementMatch currentMatch; private BindingSet bindingSet; private boolean canFork; private final int parentPath; private final int path; @Override public String toString() { return patternElement + " : " + instructionIterator + " :: " + currentMatch; } /** * Constructor. Builds the start state. * * @param basicBlock * the initial basic block * @param instructionIterator * the instructionIterator indicating where to start matching * @param patternElement * the first PatternElement of the pattern */ public State(BasicBlock basicBlock, BasicBlock.InstructionIterator instructionIterator, PatternElement patternElement) { this(null, basicBlock, instructionIterator, patternElement, 0, null, null, true); } /** * Constructor. */ public State(@Nullable State parent, BasicBlock basicBlock, BasicBlock.InstructionIterator instructionIterator, PatternElement patternElement, int matchCount, @Nullable PatternElementMatch currentMatch, @Nullable BindingSet bindingSet, boolean canFork) { this.basicBlock = basicBlock; this.instructionIterator = instructionIterator; this.patternElement = patternElement; this.matchCount = matchCount; this.currentMatch = currentMatch; this.bindingSet = bindingSet; this.canFork = canFork; this.parentPath = (parent != null) ? parent.path : -1; this.path = nextPath++; } /** * Make an exact copy of this object. */ public State duplicate() { return new State(this, basicBlock, instructionIterator, patternElement, matchCount, currentMatch, bindingSet, canFork); } /** * Get basic block. */ public BasicBlock getBasicBlock() { return basicBlock; } /** * Get current pattern element. */ public PatternElement getPatternElement() { return patternElement; } /** * Get current pattern element match. */ public PatternElementMatch getCurrentMatch() { return currentMatch; } /** * Determine if the match is complete. */ public boolean isComplete() { // We're done when we reach the end of the chain // of pattern elements. return patternElement == null; } /** * Get a ByteCodePatternMatch representing the complete match. */ public ByteCodePatternMatch getResult() { if (!isComplete()) { throw new IllegalStateException("match not complete!"); } return new ByteCodePatternMatch(bindingSet, currentMatch); } /** * Try to produce a new state that will finish matching the current * element and start matching the next element. Returns null if the * current element is not complete. */ public State advanceToNextElement() { if (!canFork || matchCount < patternElement.minOccur()) { // Current element is not complete, or we already // forked at this point return null; } // Create state to advance to matching next pattern element // at current basic block and instruction. State advance = new State(this, basicBlock, instructionIterator.duplicate(), patternElement.getNext(), 0, currentMatch, bindingSet, true); // Now that this state has forked from this element // at this instruction, it must not do so again. this.canFork = false; return advance; } /** * Determine if the current pattern element can continue to match * instructions. */ public boolean currentElementCanContinue() { return matchCount < patternElement.maxOccur(); } /** * Determine if there are more instructions in the same basic block. */ public boolean moreInstructionsInBasicBlock() { return instructionIterator.hasNext(); } /** * Match current pattern element with next instruction in basic block. * Returns MatchResult if match succeeds, null otherwise. */ public MatchResult matchNextInBasicBlock() throws DataflowAnalysisException { if (!moreInstructionsInBasicBlock()) { throw new IllegalStateException("At end of BB!"); } // Move to location of next instruction to be matched Location location = new Location(instructionIterator.next(), basicBlock); return matchLocation(location); } /** * Determine if it is possible to continue matching in a successor basic * block. */ public boolean canAdvanceToNextBasicBlock() { return currentMatch == null || currentMatch.allowTrailingEdges(); } /** * Get most recently matched instruction. */ public InstructionHandle getLastMatchedInstruction() { if (currentMatch == null) { throw new IllegalStateException("no current match!"); } return currentMatch.getMatchedInstructionInstructionHandle(); } /** * Return a new State for continuing the overall pattern match in a * successor basic block. * * @param edge * the Edge leading to the successor basic block * @param matchResult * a MatchResult representing the match of the last * instruction in the predecessor block; null if none */ public State advanceToSuccessor(Edge edge, MatchResult matchResult) { // If we have just matched an instruction, then we allow the // matching PatternElement to choose which edges are acceptable. // This allows PatternElements to select particular control edges; // for example, only continue the pattern on the true branch // of an "if" comparison. if (matchResult != null && !matchResult.getPatternElement().acceptBranch(edge, getLastMatchedInstruction())) { return null; } return new State(this, edge.getTarget(), edge.getTarget().instructionIterator(), patternElement, matchCount, currentMatch, bindingSet, canFork); } /** * Determine if we need to look for a dominated instruction at this * point in the search. */ public boolean lookForDominatedInstruction() { return patternElement.getDominatedBy() != null && matchCount == 0; } /** * Return Iterator over states representing dominated instructions that * continue the match. */ public Iterable dominatedInstructionStateIterable() throws DataflowAnalysisException { if (!lookForDominatedInstruction()) { throw new IllegalStateException(); } LinkedList stateList = new LinkedList<>(); State dup = this.duplicate(); if (currentMatch != null) { // Find the referenced instruction. PatternElementMatch dominator = currentMatch.getFirstLabeledMatch(patternElement.getDominatedBy()); BasicBlock domBlock = dominator.getBasicBlock(); InstructionHandle domInstruction = dominator.getMatchedInstructionInstructionHandle(); // Find all basic blocks dominated by the dominator block. for (Iterator i = cfg.blockIterator(); i.hasNext();) { BasicBlock block = i.next(); boolean includeInstructions = block != domBlock; BitSet dominators = domAnalysis.getResultFact(block); if (block == domBlock || dominators.get(domBlock.getLabel())) { // This block is dominated by the dominator block. // Each instruction in the block which matches the // current pattern // element is a new state continuing the match. for (Iterator j = block.instructionIterator(); j.hasNext();) { InstructionHandle next = j.next(); if (includeInstructions) { MatchResult matchResult = dup.matchLocation(new Location(next, block)); if (matchResult != null) { stateList.add(dup); dup = this.duplicate(); } } else if (next.equals(domInstruction)) { includeInstructions = true; } } } } } return stateList; } private MatchResult matchLocation(Location location) throws DataflowAnalysisException { // Get the ValueNumberFrames before and after the instruction ValueNumberFrame before = vnaDataflow.getFactAtLocation(location); ValueNumberFrame after = vnaDataflow.getFactAfterLocation(location); // Try to match the instruction against the pattern element. boolean debug = LOG.isDebugEnabled() && (!(patternElement instanceof Wild) || SHOW_WILD); if (debug) { debug((parentPath >= 0 ? parentPath + "->" : "") + path + ": Match " + patternElement + " against " + location.getHandle() + " " + (bindingSet != null ? bindingSet.toString() : "[]") + "..."); } MatchResult matchResult = patternElement.match(location.getHandle(), cpg, before, after, bindingSet); if (debug) { debug("\t" + ((matchResult != null) ? " ==> MATCH" : " ==> NOT A MATCH")); } if (matchResult != null) { // Successful match! // Update state to reflect that the match has occurred. ++matchCount; canFork = true; currentMatch = new PatternElementMatch(matchResult.getPatternElement(), location.getHandle(), location.getBasicBlock(), matchCount, currentMatch); bindingSet = matchResult.getBindingSet(); } return matchResult; } } private void debug(String s) { if (!LOG.isDebugEnabled()) { throw new IllegalStateException("Only call if DEBUG is true"); } String indent = " ".substring(0, depth); LOG.debug("{}{}", indent, s); } /** * Match a sequence of pattern elements, starting at the given one. The * InstructionIterator should generally be positioned just before the next * instruction to be matched. However, it may be positioned at the end of a * basic block, in which case nothing will happen except that we will try to * continue the match in the non-backedge successors of the basic block. */ int depth = 0; private void work(State state) throws DataflowAnalysisException { depth++; try { // Have we reached the end of the pattern? if (state.isComplete()) { // This is a complete match. if (LOG.isDebugEnabled()) { debug("FINISHED A MATCH!"); } resultList.add(state.getResult()); return; } if (LOG.isDebugEnabled()) { debug("Matching " + state.getPatternElement() + " against " + state.currentMatch); } // If we've reached the minimum number of occurrences for this // pattern element, we can advance to the next pattern element // without trying // to match this instruction again. We make sure that we only // advance to // the next element once for this matchCount. State advance = state.advanceToNextElement(); if (advance != null) { work(advance); } // If we've reached the maximum number of occurrences for this // pattern element, then we can't continue. if (!state.currentElementCanContinue()) { return; } MatchResult matchResult = null; // Are we looking for an instruction dominated by an earlier // matched instruction? if (state.lookForDominatedInstruction()) { Iterable dominatedInstructions = state.dominatedInstructionStateIterable(); for (State s : dominatedInstructions) { if (LOG.isDebugEnabled()) { debug("trying " + s); } work(s); } return; } // Is there another instruction in this basic block? if (state.moreInstructionsInBasicBlock()) { // Try to match it. matchResult = state.matchNextInBasicBlock(); if (matchResult == null) { return; } } // Continue the match at each successor instruction, // using the same PatternElement. if (state.moreInstructionsInBasicBlock()) { // Easy case; continue matching in the same basic block. work(state); } else if (state.canAdvanceToNextBasicBlock()) { // We've reached the end of the basic block. // Try to advance to the successors of this basic block, // ignoring loop backedges. Iterator i = cfg.outgoingEdgeIterator(state.getBasicBlock()); BitSet visitedSuccessorSet = new BitSet(); while (i.hasNext()) { Edge edge = i.next(); if (dfs.getDFSEdgeType(edge) == BACK_EDGE) { continue; } BasicBlock destBlock = edge.getTarget(); int destId = destBlock.getLabel(); // CFGs can have duplicate edges if (visitedSuccessorSet.get(destId)) { continue; } visitedSuccessorSet.set(destId, true); // See if we can continue matching in the successor basic // block. State succState = state.advanceToSuccessor(edge, matchResult); if (succState != null) { work(succState); } } } } finally { depth--; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy