/*
* 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 org.apache.bcel.generic.AASTORE;
import org.apache.bcel.generic.ARETURN;
import org.apache.bcel.generic.ArrayInstruction;
import org.apache.bcel.generic.CHECKCAST;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.INVOKEINTERFACE;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.PUTFIELD;
import org.apache.bcel.generic.PUTSTATIC;
public abstract class ResourceValueFrameModelingVisitor extends AbstractFrameModelingVisitor<ResourceValue, ResourceValueFrame> {
public ResourceValueFrameModelingVisitor(ConstantPoolGen cpg) {
super(cpg);
}
@Override
public ResourceValue getDefaultValue() {
return ResourceValue.notInstance();
}
/**
* Subclasses must override this to model the effect of the given
* instruction on the current frame.
*/
public abstract void transferInstruction(InstructionHandle handle, BasicBlock basicBlock) throws DataflowAnalysisException;
// Things to do:
// Automatically detect when resource instances escape:
// - putfield, putstatic
// - parameters to invoke, but subclasses may override
// - aastore; (conservative, since the dest array may not itself escape)
// - return (areturn)
private void handleFieldStore(FieldInstruction ins) {
try {
// If the resource instance is stored in a field, then it escapes
ResourceValueFrame frame = getFrame();
ResourceValue topValue = frame.getTopValue();
if (topValue.equals(ResourceValue.instance()))
frame.setStatus(ResourceValueFrame.ESCAPED);
} catch (DataflowAnalysisException e) {
throw new InvalidBytecodeException("Stack underflow", e);
}
handleNormalInstruction(ins);
}
@Override
public void visitPUTFIELD(PUTFIELD putfield) {
handleFieldStore(putfield);
}
private void handleArrayStore(ArrayInstruction ins) {
try {
// If the resource instance is stored in an array, then we consider
// it as having escaped. This is conservative; ideally we would
// check whether this array is a field or gets passed out of the
// method.
ResourceValueFrame frame = getFrame();
ResourceValue topValue = frame.getTopValue();
if (topValue.equals(ResourceValue.instance())) {
frame.setStatus(ResourceValueFrame.ESCAPED);
}
} catch (DataflowAnalysisException e) {
throw new InvalidBytecodeException("Stack underflow", e);
}
handleNormalInstruction(ins);
}
@Override
public void visitAASTORE(AASTORE arr) {
handleArrayStore(arr);
}
@Override
public void visitPUTSTATIC(PUTSTATIC putstatic) {
handleFieldStore(putstatic);
}
/**
* Override this to check for methods that it is legal to pass the instance
* to without the instance escaping. By default, we consider all methods to
* be possible escape routes.
*
* @param inv
* the InvokeInstruction to which the resource instance is passed
* as an argument
* @param instanceArgNum
* the first argument the instance is passed in
*/
protected boolean instanceEscapes(InvokeInstruction inv, int instanceArgNum) {
return true;
}
private void handleInvoke(InvokeInstruction inv) {
ResourceValueFrame frame = getFrame();
int numSlots = frame.getNumSlots();
int numConsumed = getNumWordsConsumed(inv);
// See if the resource instance is passed as an argument
int instanceArgNum = -1;
for (int i = numSlots - numConsumed, argCount = 0; i < numSlots; ++i, ++argCount) {
ResourceValue value = frame.getValue(i);
if (value.equals(ResourceValue.instance())) {
instanceArgNum = argCount;
break;
}
}
if (instanceArgNum >= 0 && instanceEscapes(inv, instanceArgNum))
frame.setStatus(ResourceValueFrame.ESCAPED);
handleNormalInstruction(inv);
}
@Override
public void visitCHECKCAST(CHECKCAST obj) {
try {
ResourceValueFrame frame = getFrame();
ResourceValue topValue;
topValue = frame.getTopValue();
if (topValue.equals(ResourceValue.instance()))
frame.setStatus(ResourceValueFrame.ESCAPED);
} catch (DataflowAnalysisException e) {
AnalysisContext.logError("Analysis error", e);
}
}
@Override
public void visitINVOKEVIRTUAL(INVOKEVIRTUAL inv) {
handleInvoke(inv);
}
@Override
public void visitINVOKEINTERFACE(INVOKEINTERFACE inv) {
handleInvoke(inv);
}
@Override
public void visitINVOKESPECIAL(INVOKESPECIAL inv) {
handleInvoke(inv);
}
@Override
public void visitINVOKESTATIC(INVOKESTATIC inv) {
handleInvoke(inv);
}
@Override
public void visitARETURN(ARETURN ins) {
try {
ResourceValueFrame frame = getFrame();
ResourceValue topValue = frame.getTopValue();
if (topValue.equals(ResourceValue.instance()))
frame.setStatus(ResourceValueFrame.ESCAPED);
} catch (DataflowAnalysisException e) {
throw new InvalidBytecodeException("Stack underflow", e);
}
handleNormalInstruction(ins);
}
}
// vim:ts=4