/****************************************************************
 *								*
 * Copyright (c) 2001-2019 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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"

#ifdef UNIX
# include "io.h"
#endif
#include "error.h"
#include "indir_enum.h"
#include <rtnhdr.h>
#include "mv_stent.h"
#include "stack_frame.h"
#include "stringpool.h"
#include "op.h"
#include "mprof.h"
#include "golevel.h"
#include "mvalconv.h"
#include "svnames.h"
#include "error_trap.h"
#include "jobinterrupt_process.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "gdskill.h"
#include "jnl.h"
#ifdef GTM_TRIGGER
# include "gv_trigger.h"
# include "gtm_trigger.h"
#endif
#include "ztimeout_routines.h"

#define POP_SPECIFIED 	(ZTRAP_POP & (TREF(ztrap_form)) && (level2go = MV_FORCE_INTD(&ztrap_pop2level))) /* note: assignment */

GBLREF stack_frame	*frame_pointer, *zyerr_frame, *error_frame;
GBLREF unsigned short	proc_act_type;
GBLREF spdesc		rts_stringpool, stringpool, indr_stringpool;
GBLREF mstr		*err_act;
GBLREF boolean_t	is_tracing_on;
GBLREF mval		dollar_zyerror, ztrap_pop2level;
GBLREF unsigned char	*msp, *stackwarn, *stacktop;
GBLREF mv_stent		*mv_chain;
GBLREF unsigned char	*restart_pc, *restart_ctxt;
GBLREF	io_desc		*gtm_err_dev;

error_def(ERR_ASSERT);
error_def(ERR_CTRAP);
error_def(ERR_GTMASSERT);
error_def(ERR_GTMASSERT2);
error_def(ERR_GTMCHECK);
error_def(ERR_MEMORY);
error_def(ERR_OUTOFSPACE);
error_def(ERR_STACKCRIT);
error_def(ERR_STACKOFLOW);
error_def(ERR_VMSMEMORY);

/* After researching the usability of the extra ztrap frame, it is found that
 * the extra frame is not really necessary and hence no more pushed onto the stack.
 * That is, trans_code does not call copy_stack_frame() anymore and neither does
 * op_unwind have to unwind the extra ztrap frame.
 * The functions affected by this change :
 *	op_unwind : Now, it does not need to pop out an extra frame in case
 *		    the curent frame is of ztrap or dev_act type.
 *	trans_code_cleanup : Should also check proc_act_type value for reporting
 *		    ztrap or dev_act error.
 */

void trans_code_finish(void);
void trans_code(void);

CONDITION_HANDLER(zyerr_ch)
{
	START_CH(TRUE);
	if (indr_stringpool.base == stringpool.base)
	{ /* switch to run time stringpool */
		indr_stringpool = stringpool;
		stringpool = rts_stringpool;
	}
	if (!DUMPABLE)
	{
		TREF(compile_time) = FALSE;
		UNWIND(NULL, NULL); /* ignore $ZYERROR compile time errors; continue with $ZTRAP (or DEV exception) action */
	} else
	{
		NEXTCH; /* serious error, can't ignore; handle error appropriately */
	}
}

void trans_code_finish(void)
{
	mval		dummy;

	frame_pointer->type = proc_act_type;
	proc_act_type = 0;
	if (0 != dollar_zyerror.str.len)
	{
		dummy.mvtype = MV_STR;
		dummy.str = dollar_zyerror.str;
		ESTABLISH(zyerr_ch);
		op_commarg(&dummy, indir_do);
		REVERT;
		op_newintrinsic(SV_ZYERROR); /* for user's convenience */
		assert(NULL == zyerr_frame);
		zyerr_frame = frame_pointer;
	}
	return;
}

CONDITION_HANDLER(trans_code_ch)
{
	mval		dummy;
	int		level2go;

	START_CH(TRUE);
	/* Treat $ZTRAP (and DEVICE exception action) as the target entryref for an implicit GOTO */
	if (DUMPABLE 				/* fatal error; we test for STACKOFLOW as part of DUMPABLE test */
	    || (int)ERR_STACKCRIT == SIGNAL 	/* Successfully compiled ${Z,E}TRAP code but encountered STACK error while
						 * attempting to push new frame, OR, STACK error while executing $ZTRAP entryref
						 */
	    || !(ZTRAP_ENTRYREF & TREF(ztrap_form))	/* User doesn't want ENTRYREF form for $ZTRAP */
	    || !(ZTRAP_CODE & TREF(ztrap_form))	/* Error during $ZTRAP ENTRYREF processing */
	    || IS_ETRAP)			/* Error compiling $ETRAP code */
	{
		NEXTCH;
	}
	assert(!TREF(compile_time) || (FALSE == TREF(transform)));
	assert(TREF(compile_time) || (TRUE == TREF(transform)));
	/* This is for when stack gets unwound in op_commarg before comp_fini gets called*/
	TREF(transform) = TRUE;
	TREF(compile_time) = FALSE;
	assert(SFT_ZTRAP == proc_act_type || SFT_DEV_ACT == proc_act_type); /* are trans_code and this function in sync? */
	assert(NULL != indr_stringpool.base); /* indr_stringpool must have been initialized by now */
	if (indr_stringpool.base == stringpool.base)
	{ /* switch to run time stringpool */
		indr_stringpool = stringpool;
		stringpool = rts_stringpool;
	}
	if (POP_SPECIFIED)
	{ /* pop to the level of last 'set $ztrap' */
		GOLEVEL(level2go, TRUE);
		/* previous trans_code_pop would have been popped out */
		dummy.mvtype = MV_STR;
		dummy.str = *err_act;
		TREF(trans_code_pop) = push_mval(&dummy);
	}
	op_commarg(TREF(trans_code_pop), indir_goto);
	if (NULL != gtm_err_dev)
	{
		if ((gtmsocket == gtm_err_dev->type) && gtm_err_dev->newly_created)
		{
			assert(gtm_err_dev->state != dev_open);
			iosocket_destroy(gtm_err_dev);
		}
#		ifdef UNIX
		if (gtmsocket != gtm_err_dev->type)
			remove_rms(gtm_err_dev);
#		endif
		gtm_err_dev = NULL;
	}
	trans_code_finish();
	UNWIND(NULL, NULL);
}

void trans_code(void)
{
	mval		dummy;
	int		level2go;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if (SFT_ZINTR & proc_act_type)
	{	/* Need different translator EP */
		jobinterrupt_process();
		return;
	}
	if ((SFT_ZTIMEOUT & proc_act_type) && ((TREF(dollar_ztimeout)).ztimeout_vector.str.len))
	{	/* Else current ETRAP or ZTRAP is the vector */
		ztimeout_process();
		return;
	}
	assert(err_act);
	if (stringpool.base != rts_stringpool.base)
	{
		indr_stringpool = stringpool;
		stringpool = rts_stringpool;
	}
	assert(SFT_ZTRAP == proc_act_type || SFT_DEV_ACT == proc_act_type);
	/* The frame_pointer->mpc of error-causing M routine should always be set
	 * to 'error_return' irrespective of the validity of $etrap code to make sure
	 * the error-occuring frame is always unwound and the new error is rethrown
	 * at one level below
	 */
	if (IS_ETRAP)
		SET_ERROR_FRAME(frame_pointer);	/* reset error_frame to point to frame_pointer */
	if (!(ZTRAP_CODE & TREF(ztrap_form)) && !IS_ETRAP && POP_SPECIFIED)
	{
		GOLEVEL(level2go, TRUE);
	}
	dummy.mvtype = MV_STR;
	dummy.str = *err_act;
	TREF(trans_code_pop) = push_mval(&dummy);
	ESTABLISH(trans_code_ch);
	op_commarg(TREF(trans_code_pop), (ZTRAP_CODE & (TREF(ztrap_form)) || IS_ETRAP) ? indir_linetail : indir_goto);
	REVERT;
	if (NULL != gtm_err_dev)
	{
		if (gtmsocket != gtm_err_dev->type)
			remove_rms(gtm_err_dev);
		else if (gtm_err_dev->newly_created)
		{
			assert(gtm_err_dev->state != dev_open);
			iosocket_destroy(gtm_err_dev);
		}
		gtm_err_dev = NULL;
	}
	trans_code_finish();
	return;
}
