package com.ibm.ive.tools.japt.inline;

import com.ibm.jikesbt.BT_Class;
import com.ibm.jikesbt.BT_Method;
import com.ibm.jikesbt.BT_MethodRefIns;

/**
 * @author sfoley
 *
 * To change this generated comment edit the template variable "typecomment":
 * Window>Preferences>Java>Templates.
 * To enable and disable the creation of type comments go to
 * Window>Preferences>Java>Code Generation.
 */
public class InstanceMethod extends Method {

	boolean assumeUnknownVirtualTargets;
	
	/**
	 * Constructor for InstanceMethod.
	 * @param method
	 */
	public InstanceMethod(
			BT_Method method, 
			InlineRepository inlineRep, 
			boolean assumeUnknownVirtualTargets,
			boolean inlineFromAnywhere) {
		super(method, inlineRep, inlineFromAnywhere);
	}

	public BT_Method resolveSpecialTarget(BT_Method containingMethod, BT_Method target) {
		
		//the target should never be abstract or have no code in well formed classes, but it does happen...
		if(target.isAbstract() || target.getCode() == null) {
			return null;
		}
		//note that an invoke special target can be overridden if:
		//-the class C is generated by a newer compiler that has the ACC_SUPER flag set
		//-the invoked method is in a superclass S of the calling method
		//-the invoked method is not a constructor
		//if those conditions are true, then the method target can be overridden by a method
		//in a superclass D, such that C extends D extends S
		
		if(target.isConstructor() || target.isPrivate()) {
			return target;
		}
		if(target.cls.isClassAncestorOf(containingMethod.cls)) {
			//calling a method in a parent class
			BT_Class superC = containingMethod.cls.getSuperClass();
			
			if(superC != null) {
				BT_Method newTarget = getRepository().getRelatedMethodMap().getOverridingMethod(superC, target);
				if(newTarget != null) {
					return newTarget;
				}
			}
		}
		return target;
	}
	
	public BT_Method resolveVirtualTarget(
			BT_Method siteTarget, 
			boolean assumeUnknownVirtualTargets) {
		if (siteTarget.isPrivate() 
			|| siteTarget.isFinal()
			|| siteTarget.cls.isFinal()) {
			return siteTarget;
		}
		if(assumeUnknownVirtualTargets || siteTarget.isAbstract() || siteTarget.getCode() == null) {
			return null;
		}
		
		//check for overriding method
		if(getRepository().getRelatedMethodMap().hasOverridingMethod(siteTarget)) {
			return null;
		}
		return siteTarget;
	}
	
	MethodCallSite getMethodCallSite(
			InliningCodeAttribute attr, 
			BT_MethodRefIns callSiteInstruction, 
			boolean overridePermissions) {
		
		if(callSiteInstruction.isInvokeVirtualIns()) {
			return new MethodCallSite(
				attr, 
				callSiteInstruction, 
				resolveVirtualTarget(
						callSiteInstruction.target, 
						assumeUnknownVirtualTargets), 
				overridePermissions,
				inlineFromAnywhere);
		}
		else if(callSiteInstruction.isInvokeInterfaceIns()) {
			return new MethodCallSite(
				attr, 
				callSiteInstruction, 
				null,
				overridePermissions,
				inlineFromAnywhere);
		}
		else { //if(callSiteInstruction.isInvokeSpecialIns())
			return new MethodCallSite(
				attr, 
				callSiteInstruction, 
				resolveSpecialTarget(
						attr.getContainingMethod(), 
						callSiteInstruction.target), 
				overridePermissions,
				inlineFromAnywhere);
		}
	}
	
	
}
