/****************************************************************
 *								*
 *	Copyright 2001, 2011 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"
#include "compiler.h"
#include "opcode.h"
#include "toktyp.h"
#include "mdq.h"
#include "advancewindow.h"

#define	INDIR_DUMMY	-1

error_def(ERR_ACTOFFSET);

int exfunc(oprtype *a, boolean_t alias_target)
{
	triple		*calltrip, *calltrip_opr1_tref, *counttrip, *funret, *labelref, *masktrip;
	triple		*oldchain, *ref0, *routineref, tmpchain, *triptr;
#	if defined(USHBIN_SUPPORTED) || defined(VMS)
	triple		*tripsize;
#	endif
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(TK_DOLLAR == TREF(window_token));
	advancewindow();
	dqinit(&tmpchain, exorder);
	oldchain = setcurtchain(&tmpchain);
	calltrip = entryref(OC_EXFUN, OC_EXTEXFUN, INDIR_DUMMY, TRUE, TRUE, FALSE);
	setcurtchain(oldchain);
	if (!calltrip)
		return FALSE;
	if (OC_EXFUN == calltrip->opcode)
	{
		assert(MLAB_REF == calltrip->operand[0].oprclass);
#		if defined(USHBIN_SUPPORTED) || defined(VMS)
		ref0 = newtriple(OC_PARAMETER);
		ref0->operand[0] = put_tsiz();		/* Need size of following code gen triple here */
		calltrip->operand[1] = put_tref(ref0);
		tripsize = ref0->operand[0].oprval.tref;
		assert(OC_TRIPSIZE == tripsize->opcode);
#		else
		ref0 = calltrip;
#		endif
	} else
	{
		calltrip_opr1_tref = calltrip->operand[1].oprval.tref;
		if (OC_EXTEXFUN == calltrip->opcode)
		{
			assert(TRIP_REF == calltrip->operand[1].oprclass);
			if (OC_CDLIT == calltrip_opr1_tref->opcode)
				assert(CDLT_REF == calltrip_opr1_tref->operand[0].oprclass);
			else
			{
				assert(OC_LABADDR == calltrip_opr1_tref->opcode);
				assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprclass);
				assert(OC_PARAMETER == calltrip_opr1_tref->operand[1].oprval.tref->opcode);
				assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprclass);
				assert(OC_ILIT == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->opcode);
				assert(ILIT_REF
				       == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprclass);
				if (0 != calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit)
				{
					stx_error(ERR_ACTOFFSET);
					return FALSE;
				}
			}
		} else		/* $$ @dlabel [actuallist] */
		{
			assert(OC_COMMARG == calltrip->opcode);
			assert(TRIP_REF == calltrip->operand[1].oprclass);
			assert(OC_ILIT == calltrip_opr1_tref->opcode);
			assert(ILIT_REF == calltrip_opr1_tref->operand[0].oprclass);
			assert(INDIR_DUMMY == calltrip_opr1_tref->operand[0].oprval.ilit);
			assert(calltrip->exorder.fl == &tmpchain);
			routineref = maketriple(OC_CURRHD);
			labelref = maketriple(OC_LABADDR);
			ref0 = maketriple(OC_PARAMETER);
			dqins(calltrip->exorder.bl, exorder, routineref);
			dqins(calltrip->exorder.bl, exorder, labelref);
			dqins(calltrip->exorder.bl, exorder, ref0);
			labelref->operand[0] = calltrip->operand[0];
			labelref->operand[1] = put_tref(ref0);
			ref0->operand[0] = calltrip->operand[1];
			ref0->operand[0].oprval.tref->operand[0].oprval.ilit = 0;
			ref0->operand[1] = put_tref(routineref);
			calltrip->operand[0] = put_tref(routineref);
			calltrip->operand[1] = put_tref(labelref);
			calltrip->opcode = OC_EXTEXFUN;
		}
		ref0 = newtriple(OC_PARAMETER);
		ref0->operand[0] = calltrip->operand[1];
		calltrip->operand[1] = put_tref(ref0);
	}
	if (TK_LPAREN != TREF(window_token))
	{
		masktrip = newtriple(OC_PARAMETER);
		counttrip = newtriple(OC_PARAMETER);
		masktrip->operand[0] = put_ilit(0);
		counttrip->operand[0] = put_ilit(0);
		masktrip->operand[1] = put_tref(counttrip);
		ref0->operand[1] = put_tref(masktrip);
	} else
		if (!actuallist(&ref0->operand[1]))
			return FALSE;
	triptr = oldchain->exorder.bl;
	dqadd(triptr, &tmpchain, exorder);		/*this is a violation of info hiding*/
	if (OC_EXFUN == calltrip->opcode)
	{
		assert(MLAB_REF == calltrip->operand[0].oprclass);
		triptr = newtriple(OC_JMP);
		triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname);
		calltrip->operand[0].oprclass = ILIT_REF;	/* dummy placeholder */
#		if defined(USHBIN_SUPPORTED) || defined(VMS)
		tripsize->operand[0].oprval.tsize->ct = triptr;
#		endif
	}
	/* If target is an alias, use special container-expecting routine OC_EXFUNRETALS, else regular OC_EXFUNRET */
	funret = newtriple((alias_target ? OC_EXFUNRETALS : OC_EXFUNRET));
	funret->operand[0] = *a = put_tref(calltrip);
	return TRUE;
}
