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