/*
 * 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.ARETURN;
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
	//   - 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);
	}

	@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 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

@@ -897,16 +897,58 @@
 RETURN;%0A
+import org.apache.bcel.generic.CHECKCAST;%0A
 import o
@@ -3959,32 +3959,484 @@
 ction(inv);%0A%09%7D%0A%0A
+    @Override%0A    public void visitCHECKCAST(CHECKCAST obj) %7B%0A        try %7B%0A            ResourceValueFrame frame = getFrame();%0A            ResourceValue topValue;%0A%0A            topValue = frame.getTopValue();%0A%0A            if (topValue.equals(ResourceValue.instance()))%0A                frame.setStatus(ResourceValueFrame.ESCAPED);%0A        %7D catch (DataflowAnalysisException e) %7B%0A            AnalysisContext.logError(%22Analysis error%22, e);%0A        %7D%0A    %7D%0A
 %09@Override%0A     

fix [ 1578378 ] ODR false positive with CallableStatement subclass