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

edu.umd.cs.findbugs.ba.Frame 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;

import static edu.umd.cs.findbugs.ba.Debug.VERIFY_INTEGRITY;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;

import org.apache.bcel.Const;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.StackConsumer;

import edu.umd.cs.findbugs.SystemProperties;

/**
 * 

Generic class for representing a Java stack frame as a dataflow value. A * frame consists of "slots", which represent the local variables and values on * the Java operand stack. Slots 0 .. getNumLocals() - 1 represent * the local variables. Slots getNumLocals() .. * getNumSlots() - 1 represent the Java operand stack. *

*

* Frame is parametized by "ValueType", which is the type of value to be stored * in the Frame's slots. This type must form a lattice, according to the * abstract mergeValues() operation in the corresponding analysis class (which * should be derived from FrameDataflowAnalysis). When a Frame is constructed, * all of its slots will contain null. The analysis is responsible for * initializing created Frames with default values at the appropriate time. * Typically, only initEntryFact() will need to do this. *

*

* A Frame may have the special "TOP" value. Such frames serve as the identity * element for the meet operation operation. *

*

* A Frame may have the special "BOTTOM" value. The result of merging any frame * with BOTTOM is BOTTOM.

* * @author David Hovemeyer * @see FrameDataflowAnalysis */ public abstract class Frame { // ////////////////////////////////////////////////////////////////////////////////// // Instance variables // ////////////////////////////////////////////////////////////////////////////////// private int lastUpdateTimestamp; /** * Number of local variables in the method. */ private final int numLocals; /** * Array storing the values of local variables and operand stack slots. */ private ArrayList slotList; /** * Flag marking this frame as a special "TOP" value. Such Frames serve as * the identity element when merging. */ private boolean isTop; /** * Flag marking this frame as a special "BOTTOM" value. Such Frames arise * when merging two frames of different size. */ private boolean isBottom; /** * Default number of stack slots to preallocate space for. */ private static final int DEFAULT_STACK_CAPACITY = 4; // ////////////////////////////////////////////////////////////////////////////////// // Methods // ////////////////////////////////////////////////////////////////////////////////// /** * Constructor. This version of the constructor is for subclasses for which * it is always safe to call getDefaultValue(), even when the object is not * fully initialized. * * @param numLocals * number of local variable slots in the method */ public Frame(int numLocals) { this.numLocals = numLocals; this.slotList = new ArrayList<>(numLocals + DEFAULT_STACK_CAPACITY); for (int i = 0; i < numLocals; ++i) { slotList.add(null); } } /** * Return whether or not this object the special "TOP" value for Frames. * Such Frames are the identity element of the meet operation. */ public boolean isTop() { return isTop; } /** * Make this frame the special "TOP" value. Such Frames are the identity * element of the meet operation. */ public void setTop() { isTop = true; isBottom = false; lastUpdateTimestamp = 0; } /** * Return whether or not this object is the special "BOTTOM" value for * Frames. Such Frames arise when merging two frames of different size. */ public boolean isBottom() { return isBottom; } /** * Make this Frame the special "BOTTOM" value. Such Frames arise when * merging two frames of different size. */ public void setBottom() { isBottom = true; isTop = false; } /** * Set the Frame to be valid (neither TOP nor BOTTOM). */ public void setValid() { isTop = isBottom = false; } /** * Is the frame valid (meaning it is not TOP or BOTTOM)? */ public boolean isValid() { return !isTop() && !isBottom(); } /** * Push a value onto the Java operand stack. * * @param value * the ValueType to push */ public void pushValue(ValueType value) { if (VERIFY_INTEGRITY && value == null) { throw new IllegalArgumentException(); } if (!isValid()) { throw new IllegalStateException("accessing top or bottom frame"); } slotList.add(value); } /** * Pop a value off of the Java operand stack. * * @return the value that was popped * @throws DataflowAnalysisException * if the Java operand stack is empty */ public ValueType popValue() throws DataflowAnalysisException { if (!isValid()) { throw new DataflowAnalysisException("accessing top or bottom frame"); } if (slotList.size() == numLocals) { throw new DataflowAnalysisException("operand stack empty"); } return slotList.remove(slotList.size() - 1); } /** * Get the value on the top of the Java operand stack. * * @throws DataflowAnalysisException * if the Java operand stack is empty */ public ValueType getTopValue() throws DataflowAnalysisException { if (!isValid()) { throw new DataflowAnalysisException("accessing top or bottom frame"); } assert slotList.size() >= numLocals; if (slotList.size() == numLocals) { throw new DataflowAnalysisException("operand stack is empty"); } return slotList.get(slotList.size() - 1); } /** * Get the values on the top of the Java operand stack. The top stack item * is placed at the end of the array, so that to restore the values to the * stack, you would push them in the order they appear in the array. */ public void getTopStackWords(ValueType[] valueList) throws DataflowAnalysisException { int stackDepth = getStackDepth(); if (valueList.length > stackDepth) { throw new DataflowAnalysisException("not enough values on stack"); } int numSlots = slotList.size(); for (int i = numSlots - valueList.length, j = 0; i < numSlots; ++i, ++j) { valueList[j] = slotList.get(i); } } /** * Get a value on the operand stack. * * @param loc * the stack location, counting downwards from the top (location * 0) */ public ValueType getStackValue(int loc) throws DataflowAnalysisException { if (!isValid()) { throw new DataflowAnalysisException("Accessing TOP or BOTTOM frame!"); } int stackDepth = getStackDepth(); if (loc >= stackDepth) { throw new DataflowAnalysisException("not enough values on stack: access=" + loc + ", avail=" + stackDepth); } if (loc < 0) { throw new DataflowAnalysisException("can't get position " + loc + " of stack"); } int pos = slotList.size() - (loc + 1); return slotList.get(pos); } /** * Get a the location in the frame of a value on the operand stack. * * @param loc * the stack location, counting downwards from the top (location * 0) */ public int getStackLocation(int loc) throws DataflowAnalysisException { int stackDepth = getStackDepth(); if (loc >= stackDepth) { throw new DataflowAnalysisException("not enough values on stack: access=" + loc + ", avail=" + stackDepth); } return slotList.size() - (loc + 1); } /** * Get the value corresponding to the object instance used in the given * instruction. This relies on the observation that in instructions which * use an object instance (such as getfield, invokevirtual, etc.), the * object instance is the first operand used by the instruction. * * @param ins * the instruction * @param cpg * the ConstantPoolGen for the method */ public ValueType getInstance(Instruction ins, ConstantPoolGen cpg) throws DataflowAnalysisException { return getStackValue(getInstanceStackLocation(ins, cpg)); } /** * Get the stack location (counting down from top of stack, starting at 0) * containing the object instance referred to by given instruction. This * relies on the observation that in instructions which use an object * instance (such as getfield, invokevirtual, etc.), the object instance is * the first operand used by the instruction. * *

* The value returned may be passed to getStackValue(int). *

* * @param ins * the Instruction * @param cpg * the ConstantPoolGen for the method * @return stack location (counting down from top of stack, starting at 0) * containing the object instance * @throws DataflowAnalysisException */ public int getInstanceStackLocation(Instruction ins, ConstantPoolGen cpg) throws DataflowAnalysisException { int numConsumed = ins.consumeStack(cpg); if (numConsumed == Const.UNPREDICTABLE) { throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins); } return numConsumed - 1; } /** * Get the slot the object instance referred to by given instruction is * located in. * * @param ins * the Instruction * @param cpg * the ConstantPoolGen for the method * @return stack slot the object instance is in * @throws DataflowAnalysisException */ public int getInstanceSlot(Instruction ins, ConstantPoolGen cpg) throws DataflowAnalysisException { if (!isValid()) { throw new DataflowAnalysisException("Accessing invalid frame at " + ins); } int numConsumed = ins.consumeStack(cpg); if (numConsumed == Const.UNPREDICTABLE) { throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins); } if (numConsumed > getStackDepth()) { throw new DataflowAnalysisException("Stack underflow " + ins); } return getNumSlots() - numConsumed; } /** * Get the number of arguments passed to given method invocation. * * @param ins * the method invocation instruction * @param cpg * the ConstantPoolGen for the class containing the method * @return number of arguments; note that this excludes the object instance * for instance methods */ public int getNumArguments(InvokeInstruction ins, ConstantPoolGen cpg) { SignatureParser parser = new SignatureParser(ins.getSignature(cpg)); return parser.getNumParameters(); } /** * Get the number of arguments passed to given method invocation, including * the object instance if the call is to an instance method. * * @param ins * the method invocation instruction * @param cpg * the ConstantPoolGen for the class containing the method * @return number of arguments, including object instance if appropriate * @throws DataflowAnalysisException */ public int getNumArgumentsIncludingObjectInstance(InvokeInstruction ins, ConstantPoolGen cpg) throws DataflowAnalysisException { int numConsumed = ins.consumeStack(cpg); if (numConsumed == Const.UNPREDICTABLE) { throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins); } return numConsumed; } /** * Get the ith argument passed to given method invocation. * * @param ins * the method invocation instruction * @param cpg * the ConstantPoolGen for the class containing the method * @param i * index of the argument; 0 for the first argument, etc. * @param numArguments * total number of arguments to the method * @return the ith argument * @throws DataflowAnalysisException */ @Deprecated public ValueType getArgument(InvokeInstruction ins, ConstantPoolGen cpg, int i, int numArguments) throws DataflowAnalysisException { SignatureParser sigParser = new SignatureParser(ins.getSignature(cpg)); return getArgument(ins, cpg, i, sigParser); } /** * Get the ith argument passed to given method invocation. * * @param ins * the method invocation instruction * @param cpg * the ConstantPoolGen for the class containing the method * @param i * index of the argument; 0 for the first argument, etc. * @return the ith argument * @throws DataflowAnalysisException */ public ValueType getArgument(InvokeInstruction ins, ConstantPoolGen cpg, int i, SignatureParser sigParser) throws DataflowAnalysisException { if (i >= sigParser.getNumParameters()) { throw new IllegalArgumentException("requesting parameter # " + i + " of " + sigParser); } return getStackValue(sigParser.getSlotsFromTopOfStackForParameter(i)); } /** * Get the stack slot that will contain given method argument. Assumes that * this frame is at the location (just before) a method invocation * instruction. * * @param i * the argument index: 0 for first arg, etc. * @param numArguments * total number of arguments to the called method * @return slot containing the argument value */ public int getArgumentSlot(int i, int numArguments) { if (i >= numArguments) { throw new IllegalArgumentException(); } return (slotList.size() - numArguments) + i; } /** * Get the ith operand used by given instruction. * * @param ins * the instruction, which must be a StackConsumer * @param cpg * the ConstantPoolGen * @param i * index of operand to get: 0 for the first operand, etc. * @return the ith operand used by the given instruction * @throws DataflowAnalysisException */ public ValueType getOperand(StackConsumer ins, ConstantPoolGen cpg, int i) throws DataflowAnalysisException { int numOperands = ins.consumeStack(cpg); if (numOperands == Const.UNPREDICTABLE) { throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins); } return getStackValue((numOperands - 1) - i); } /** * Get set of arguments passed to a method invocation which match given * predicate. * * @param invokeInstruction * the InvokeInstruction * @param cpg * the ConstantPoolGen * @param chooser * predicate to choose which argument values should be in the * returned set * @return BitSet specifying which arguments match the predicate, indexed by * argument number (starting from 0) * @throws DataflowAnalysisException */ public BitSet getArgumentSet(InvokeInstruction invokeInstruction, ConstantPoolGen cpg, DataflowValueChooser chooser) throws DataflowAnalysisException { BitSet chosenArgSet = new BitSet(); SignatureParser sigParser = new SignatureParser(invokeInstruction.getSignature(cpg)); for (int i = 0; i < sigParser.getNumParameters(); ++i) { ValueType value = getArgument(invokeInstruction, cpg, i, sigParser); if (chooser.choose(value)) { chosenArgSet.set(i); } } return chosenArgSet; } /** * Clear the Java operand stack. Only local variable slots will remain in * the frame. */ public void clearStack() { if (!isValid()) { throw new IllegalStateException("accessing top or bottom frame"); } assert slotList.size() >= numLocals; if (slotList.size() > numLocals) { slotList.subList(numLocals, slotList.size()).clear(); } } /** * Get the depth of the Java operand stack. */ public int getStackDepth() { return slotList.size() - numLocals; } /** * Get the number of locals. */ public int getNumLocals() { return numLocals; } /** * Get the number of slots (locals plus stack values). */ public int getNumSlots() { return slotList.size(); } public boolean contains(ValueType value) { if (!isValid()) { throw new IllegalStateException("accessing top or bottom frame"); } for (ValueType v : slotList) { if (v.equals(value)) { return true; } } return false; } /** * Get the value at the nth slot. * * @param n * the slot to get the value of * @return the value in the slot */ public ValueType getValue(int n) { if (!isValid()) { throw new IllegalStateException("accessing top or bottom frame"); } return slotList.get(n); } /** * Set the value at the nth slot. * * @param n * the slot in which to set a new value * @param value * the value to set */ public void setValue(int n, ValueType value) { if (VERIFY_INTEGRITY && value == null) { throw new IllegalArgumentException(); } if (!isValid()) { throw new IllegalStateException("accessing top or bottom frame"); } slotList.set(n, value); } /** * Return true if this stack frame is the same as the one given as a * parameter. * * @param other * the other Frame * @return true if the frames are the same, false otherwise */ public boolean sameAs(Frame other) { if (isTop != other.isTop) { return false; } if (isTop && other.isTop) { return true; } if (isBottom != other.isBottom) { return false; } if (isBottom && other.isBottom) { return true; } if (getNumSlots() != other.getNumSlots()) { return false; } for (int i = 0; i < getNumSlots(); ++i) { if (!getValue(i).equals(other.getValue(i))) { return false; } } return true; } /** * Make this Frame exactly the same as the one given as a parameter. * * @param other * the Frame to make this object the same as */ public void copyFrom(Frame other) { lastUpdateTimestamp = other.lastUpdateTimestamp; slotList = new ArrayList<>(other.slotList); isTop = other.isTop; isBottom = other.isBottom; } private static final boolean STACK_ONLY = SystemProperties.getBoolean("dataflow.stackonly"); /** * Convert to string. */ @Override public String toString() { if (isTop()) { return "[TOP]"; } if (isBottom()) { return "[BOTTOM]"; } StringBuilder buf = new StringBuilder(); buf.append('['); int numSlots = getNumSlots(); int start = STACK_ONLY ? getNumLocals() : 0; for (int i = start; i < numSlots; ++i) { if (!STACK_ONLY && i == getNumLocals()) { // Use a "|" character to visually separate locals from // the operand stack. int last = buf.length() - 1; if (last >= 0 && buf.charAt(last) == ',') { buf.deleteCharAt(last); } buf.append('|'); } String value = valueToString(getValue(i)); if (i == numSlots - 1 && value.endsWith(",")) { value = value.substring(0, value.length() - 1); } buf.append(value); // buf.append(' '); } buf.append(']'); return buf.toString(); } /** * Subclasses may override this if they want to do something special to * convert Value objects to Strings. By default, we just call toString() on * the values. */ protected String valueToString(ValueType value) { if (value == null) { return "null"; } return value.toString(); } /** * @return an unmodifiable Collection of the local variable and operand * stack slots */ public Collection allSlots() { if (slotList == null) { return Collections.emptyList(); } return Collections.unmodifiableCollection(slotList); } /** * @param lastUpdateTimestamp * The lastUpdateTimestamp to set. */ public void setLastUpdateTimestamp(int lastUpdateTimestamp) { this.lastUpdateTimestamp = lastUpdateTimestamp; } /** * @return Returns the lastUpdateTimestamp. */ public int getLastUpdateTimestamp() { return lastUpdateTimestamp; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy