/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1989-1999 Microsoft Corporation

 Module Name:
	
	procana.cxx

 Abstract:

	This file provides analysis routines for a procedure code generation
	class.

 Notes:

 History:


	Aug-31-1993		VibhasC		Created.

 ----------------------------------------------------------------------------*/

/****************************************************************************
 *	include files
 ***************************************************************************/
#include "allana.hxx"
#pragma hdrstop

#pragma warning ( disable :  4701 )

/****************************************************************************
 	Implementation of the proc code generator class.
 ****************************************************************************/

CG_STATUS
CG_PROC::C_BindingAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform the binding analysis for the client side.

 Arguments:
	
 Return Value:
	
 Notes:

	If it is an auto handle procedure, make sure the global auto handle is
	registered as a global resource.
----------------------------------------------------------------------------*/
{
	node_id	*	pID;

	if( IsAutoHandle() )
		{
		pAna->AddStandardResource( ST_RES_AUTO_BH_VARIABLE );
		}
	else
		{
		SetBindingResource( pAna->AddStandardResource( ST_RES_BH_VARIABLE ) );

		// Initialize the binding resource to 0, so it gets printed out.

		pID	= (node_id *)GetBindingResource()->GetType();

		pID->SetExpr( new expr_constant( 0L ) );

		}
	return CG_OK;
}

CG_STATUS
CG_PROC::MarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	ITERATOR		I;
	CG_PARAM	*	pCG = 0;
	CG_PARAM	*	pS;
	CG_RETURN	*	pRT;
	SIDE			Side	= pAna->GetCurrentSide();
	expr_node	*	pSE		= 0;
	BOOL			fReturnNeedsMarshall = FALSE;

	if( Side == C_SIDE )
		GetInParamList( I );
	else
		GetOutParamList( I );

    pRT = GetReturnType();
	if ( Side == S_SIDE  && pRT != 0 ) 
		fReturnNeedsMarshall = TRUE;

	if( ITERATOR_GETCOUNT( I ) )
		{
		ITERATOR_INIT( I );

		while( ITERATOR_GETNEXT( I, pCG ) )
			{
            // The "invisible" fault or comm status param doesn't marshal.

			pS = (CG_PARAM *)ITERATOR_PEEKTHIS( I );

            if ( pCG->IsExtraStatusParam() )
                continue;

			pCG->MarshallAnalysis( pAna );

            // Add to the sizing expression.

			if( pSE )
				pSE	= new expr_b_arithmetic( OP_PLUS,
											  pSE,
											  pCG->GetSizeExpression()
											);
			else
				pSE	= pCG->GetSizeExpression();
			}
		}

	if(fReturnNeedsMarshall)
		{
                
                pRT->MarshallAnalysis( pAna );
		
                if( pSE )
			pSE	= new expr_b_arithmetic( OP_PLUS,
										  pSE,
										  pRT->GetSizeExpression()
										);
		else
			pSE	= pRT->GetSizeExpression();
		}

	if( pSE )
		SetSizeExpression( pSE );
	else
		SetSizeExpression( new expr_constant( 0L,VALUE_TYPE_NUMERIC_U ));

    if ( HasExtraStatusParam() )
        {
        GetMembers(I);

        while ( ITERATOR_GETNEXT( I, pCG ) )
            if ( pCG->IsExtraStatusParam() )
                break;

        node_skl *pType = pCG->GetType();

        pCG->SetResource( new RESOURCE( pType->GetSymName(), pType ) );
        }

	return CG_OK;
	}

CG_STATUS
CG_PROC::UnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
	{
	ITERATOR		I;
	CG_PARAM	*	pCG = 0;
	CG_PARAM	*	pS;
	CG_RETURN	*	pRT = 0;
	SIDE			Side = pAna->GetCurrentSide();
	BOOL			fReturnNeedsUnMarshall	= FALSE;

	if( Side == C_SIDE )
		GetOutParamList( I );
	else
		GetInParamList( I );
	
	if( (Side == C_SIDE ) && (pRT = GetReturnType() ) != 0 )
		fReturnNeedsUnMarshall = TRUE;

	if( ITERATOR_GETCOUNT(I) )
		{
		while( ITERATOR_GETNEXT( I, pCG ) )
			{
            // The "invisible" fault/comm status param doesn't unmarshal.

			pS = (CG_PARAM *)ITERATOR_PEEKTHIS( I );

            if ( pCG->IsExtraStatusParam() )
                continue;

			pCG->UnMarshallAnalysis( pAna );
			}

		}

	if( fReturnNeedsUnMarshall )
		{
		pRT->UnMarshallAnalysis( pAna );
		}
	

	return CG_OK;
	}


CG_STATUS
CG_PROC::S_OutLocalAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform analysis for all params which may be allocated as locals for
 	the server side stubs.

 Arguments:

 	pAna	- The analysis block.

	
 Return Value:
	
 	CG_OK	if all is well,
 	error	otherwise.

 Notes:

----------------------------------------------------------------------------*/
{

	ITERATOR		I;
	CG_PARAM	*	pParam = 0;

	GetOutParamList( I );

	while( ITERATOR_GETNEXT( I, pParam ) )
		{
		pParam->S_OutLocalAnalysis( pAna );
		}

	return CG_OK;
}


void
CG_PROC::RpcSsPackageAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform analysis for the need to invoke RpcSsm package at server.

 Arguments:

 	pAna	- The analysis block.

	
 Return Value:
	
 Notes:

   Note that we perform both sides analysis at once as the format string
   is generated usually at the client pass.

----------------------------------------------------------------------------*/
{
    //
    // In ms_ext mode:
    //    only if  Enable allocate is specified 
    // In osf mode:
    //    if( RpcSS is recommended by analysis || enable allocate specified )
    //
    SetMustInvokeRpcSSAllocate( 0 );

    if ( IsRpcSSSpecified() )
        SetMustInvokeRpcSSAllocate( 1 );

    if ( MustInvokeRpcSSAllocate()  ||  pAna->GetMode() != 0 )
        return;

    // We analyze parameters in osf to boost performance by skipping
    // unnecessary enable and disable operations.
    //
    
    ITERATOR        ParamList;
    CG_PARAM    *    pParam = 0;

    GetMembers( ParamList );

    if( ITERATOR_GETCOUNT( ParamList ) )
        {
        ITERATOR_INIT( ParamList );

        while( ITERATOR_GETNEXT( ParamList, pParam ) )
            {
            pParam->RpcSsPackageAnalysis( pAna );
            }
        }

    CG_RETURN * pReturn = GetReturnType();

    if ( pReturn )
        {
        if ( (pAna->GetOptimOption() & OPTIMIZE_INTERPRETER)  ||
             (pReturn->GetChild()  &&
                   ( ((CG_NDR *)pReturn->GetChild())->IsPointer()  ||
                     ((CG_NDR *)pReturn->GetChild())->HasPointer() ||
                     ((CG_NDR *)pReturn->GetChild())->GetCGID() == ID_CG_ENCAP_STRUCT ) )
           )
            {
            // We could do a better job for Oi2 if we watched its stack.
            // Encapsulated union is there as it was a hassle to make it
            // know about its pointers.

            pAna->SetRpcSSAllocateRecommended( 1 );
            }
        }

    if ( pAna->IsRpcSSAllocateRecommended() )
       SetMustInvokeRpcSSAllocate( 1 );
}


CG_STATUS
CG_PROC::RefCheckAnalysis(
    ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform ref pointer check analysis.

 Arguments:

 	pAna	- The analysis info block.

 Return Value:

	CG_OK
	
 Notes:

----------------------------------------------------------------------------*/
	{
	ITERATOR		I;
	CG_PARAM	*	pCG = 0;
	CG_RETURN	*	pRT = 0;
	SIDE			Side	= pAna->GetCurrentSide();
	BOOL			fReturnNeedsMarshall = FALSE;

    // generate ref check resources for both in and out params.
    GetMembers( I );
	if( (Side == S_SIDE ) && (pRT = GetReturnType() ) != 0 )
		fReturnNeedsMarshall = TRUE;

	if( ITERATOR_GETCOUNT( I ) )
		{
		ITERATOR_INIT( I );

		while( ITERATOR_GETNEXT( I, pCG ) )
			{
			pCG->RefCheckAnalysis( pAna );
			}
		}

	if(fReturnNeedsMarshall)
		{
		pRT->RefCheckAnalysis( pAna );
		}
	return CG_OK;
	}

CG_STATUS
CG_PROC::InLocalAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform analysis for [in] params allocated as locals on server.

 Arguments:

 	pAna	- The analysis info block.

 Return Value:

	CG_OK
	
 Notes:

----------------------------------------------------------------------------*/
	{
	ITERATOR		I;
	CG_PARAM	*	pCG = 0;

	GetInParamList( I );

	if( ITERATOR_GETCOUNT( I ) )
		{
		ITERATOR_INIT( I );

		while( ITERATOR_GETNEXT( I, pCG ) )
			{
			pCG->InLocalAnalysis( pAna );
			}
		}
	return CG_OK;
	}

/****************************************************************************
 	Implementation of the parameter code generator class.
 ****************************************************************************/
CG_STATUS
CG_PARAM::MarshallAnalysis(
	 ANALYSIS_INFO * pAna )
{
	CG_STATUS				Status;

	//
	// Initialize the analysis block and the parameter for the current side
	// and analysis phase.
	//

	InitParamMarshallAnalysis( pAna );

	// Send the message to the lower cg nodes.

	Status = ((CG_NDR *)GetChild())->MarshallAnalysis( pAna );

	if( pAna->HasAtLeastOneDeferredPointee() )
		{
		pAna->ResetDeferPointee();
		((CG_NDR *)GetChild())->FollowerMarshallAnalysis( pAna );
		}


	ConsolidateParamMarshallAnalysis( pAna );


	// The analysis block will now have properties which are a combo of the
	// properties before this param and the properties of this param.

	return CG_OK;
}

CG_STATUS
CG_PARAM::UnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
{

	InitParamUnMarshallAnalysis( pAna );

	// Send the message to the child to perform the same analysis for us
	// and then consolidate the results on return.

	((CG_NDR *)GetChild())->UnMarshallAnalysis( pAna );

	if( pAna->HasAtLeastOneDeferredPointee() )
		{
		pAna->ResetDeferPointee();
		((CG_NDR *)GetChild())->FollowerUnMarshallAnalysis( pAna );
		}

	// Consolidate the results of analysis from the lower nodes.

	ConsolidateParamUnMarshallAnalysis( pAna );

	return CG_OK;
}

void
CG_PARAM::InitParamMarshallAnalysis(
	ANALYSIS_INFO * pAna )
{

	node_skl	*	pType	= GetType();
	PNAME			pName	= pType->GetSymName();
	CG_NDR		*	pC		= (CG_NDR *)GetChild();

	// For all cases of generic handles, where the CG_GENERIC_HANDLE will
	// sit below the param, we want to bypass the generic handle class during
	// marshall analysis.

	if( pC->GetCGID() == ID_CG_GENERIC_HDL )
		{
		pC = (CG_NDR *)pC->GetChild();
		}

	//
	// Allocate the resource for this parameter. On the client side, this
	// parameter is a param resource, while on the server, this param is a local
	// resource. One the client side, the param has already been added to the
	// resource dictionary.

	if( pAna->GetCurrentSide() == C_SIDE )
		{
		if( (pC->IsArray()) &&
			(pAna->GetOptimOption() & OPTIMIZE_INTERPRETER )
		  )
		  	{
			pType	= MakePtrIDNode( pName, pType->GetChild() );
		  	}
		SetResource( new RESOURCE( pName, pType) );
		}
	else
		{
		node_skl	*	pActualType	= pType->GetChild();

		if( (pC->GetCGID() == ID_CG_CONTEXT_HDL ) ||
			(pC->GetChild() && (pC->GetChild()->GetCGID() == ID_CG_CONTEXT_HDL)))
			{
			pActualType	= MakeIDNode( pName, new node_def ("NDR_SCONTEXT" ) );
			}
		else if( pC->IsArray() )
			pActualType	= MakePtrIDNode( pName, pActualType);
		else
			pActualType = MakeIDNode( pName, pActualType );

		SetResource( pAna->AddLocalResource( pName, pActualType ));
		}

	SetSizeResource( 0 );
	SetLengthResource( 0 );
	SetFirstResource( 0 );

	pAna->SetMemoryAllocDone();
	pAna->ResetRefAllocDone();
	pAna->ResetEmbeddingLevel();
	pAna->ResetIndirectionLevel();
	pAna->SetRefChainIntact();
	pAna->ResetDeferPointee();
	pAna->ResetHasAtLeastOneDeferredPointee();
	pAna->SetLastPlaceholderClass( (CG_NDR *)this );
}

void
CG_PARAM::ConsolidateParamMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
{
	expr_node		*	pSE		= 0;
	

	// Consolidate the result of the analysis from lower nodes, first into
	// this node, and then into the analysis block.

	pSE	= new expr_constant( (long)0U, VALUE_TYPE_NUMERIC_U );

	SetSizeExpression( pSE );

	if( pAna->GetOptimOption() & OPTIMIZE_SIZE )
		{
		pAna->ClearTransientResourceDict();
		}
}

void
CG_PARAM::InitParamUnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
{

	node_skl	*	pType	= GetType();
	node_skl	*	pActualType = pType->GetBasicType();
	PNAME			pName	= pType->GetSymName();
	CG_NDR		*	pC		= (CG_NDR *)GetChild();

	// For all cases of generic handles, where the CG_GENERIC_HANDLE will
	// sit below the param, we want to bypass the generic handle class during
	// marshall analysis.

	if( pC->GetCGID() == ID_CG_GENERIC_HDL )
		{
		pC = (CG_NDR *)pC->GetChild();
		}

	//
	// Allocate resources. On the client side, a parameter resource is allocated
	// and on the server, a local resource is allocated.
	//

	SetSubstitutePtrResource( 0 );

	if( pAna->GetCurrentSide() == C_SIDE )
		{
		pAna->SetDontReUseBuffer();
		if( (pC->IsArray()) &&
			(pAna->GetOptimOption() & OPTIMIZE_INTERPRETER )
		  )
		  	{
			pType	= MakePtrIDNode( pName, pType->GetChild() /* GetBasicType() ????????? */ );
		  	}
		SetResource( new RESOURCE( pName, pType) ) ;
		}
	else if ( ! IsOmittedParam() )
		{
		pC->SetAllocatedOnStack( 1 );

		if( (pC->GetCGID() == ID_CG_CONTEXT_HDL ) ||
			(pC->GetChild() && (pC->GetChild()->GetCGID() == ID_CG_CONTEXT_HDL)))
			{
			pActualType	= MakeIDNode( pName, new node_def ("NDR_SCONTEXT" ) );
			}
		else if( pC->IsArray() )
			{
			pActualType	= MakePtrIDNode( pName, pType->GetChild() );
			}
		else
			pActualType	= MakeIDNode( pName, pType->GetChild() );

		pAna->ResetDontReUseBuffer();
		SetResource( pAna->AddLocalResource( pName, pActualType ) );
		}

	SetSizeResource( 0 );
	SetLengthResource( 0 );
	SetFirstResource( 0 );

	// Reset for analysis.


	pAna->SetMemoryAllocDone();
	pAna->ResetRefAllocDone();
	pAna->ResetEmbeddingLevel();
	pAna->ResetIndirectionLevel();
	pAna->SetRefChainIntact();
	pAna->ResetDeferPointee();
	pAna->ResetHasAtLeastOneDeferredPointee();
	pAna->SetLastPlaceholderClass( (CG_NDR *)this );
}

void
CG_PARAM::ConsolidateParamUnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
{

	// Consolidate the result of the analysis from lower nodes, first into
	// this node, and then into the analysis block.

	if( pAna->GetOptimOption() & OPTIMIZE_SIZE )
		{
		pAna->ClearTransientResourceDict();
		}
}
CG_STATUS
CG_PARAM::S_OutLocalAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform analysis for all params which may be allocated as locals for
 	server stub

 Arguments:
	
	pAna	- A pointer to the analysis block.

 Return Value:
	
 Notes:

	Ignore [in, out], since that would be done by the Unmarshall analysis.
----------------------------------------------------------------------------*/
{
	// If the param is [out] only, determine if there is a need for local
	// variables and if they need to be inited.
	
	if( IsParamOut() && !IsParamIn() )
		{
		InitParamMarshallAnalysis( pAna );

		((CG_NDR *)GetChild())->S_OutLocalAnalysis( pAna );

		}
	return CG_OK;
}


void
CG_PARAM::RpcSsPackageAnalysis(
    ANALYSIS_INFO    *    pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

     Perform analysis for rpcss package enabling.

 Arguments:
    
    pAna    - A pointer to the analysis block.

 Return Value:
    
 Notes:

    This routine should be called only in the osf mode.

----------------------------------------------------------------------------*/
{
    //
    // Package needs to be enabled (only is OSF mode) when the NDR engine
    // is going to do any allocations whatsoever at the server side.
    //
    // In Os this may happen on
    //    - anything other than simple type or pointer to simple type
    //    - with simple type, if this happens to be enum16
    // In Oi this may happen on
    //    - anything [out]
    //    - [in] like Os
    // In Oi2 this may happen on
    //    - anything [out] when lack of space on -Oi2 stack
    //    - [in] like Os
    //      (simple types and pointers to simple types are usually on
    //      the interpreter stack, if there is space there).
    //      However, this cannot be guaranteed.
    
    if( IsParamOut() &&
        ( pAna->GetOptimOption() & OPTIMIZE_INTERPRETER) )
        {
        // We could do a better job for Oi2 if we watched the its stack.

        pAna->SetRpcSSAllocateRecommended( 1 );
        return;
        }

    // We are here with
    //   [in] or [in,out] for Oi? 
    //   any              for Os
    // See if this is one of the simple cases mentioned above.
    // This is really a check for the buffer reusage.

    CG_NDR * pChild = (CG_NDR *)GetChild();

    if ( pChild->IsSimpleType() &&
         ( pChild->GetCGID() != ID_CG_ENUM  ||
           pChild->GetCGID() == ID_CG_ENUM  &&
                                     ((CG_ENUM*)pChild)->IsEnumLong() )
         &&  pChild->GetCGID() != ID_CG_INT3264 
       )
        {
        // An [in] arg in the buffer.

        return;
        }

    // Note that we don't have to check for allocate(allnodes) etc.
    // as this is not an osf attribute.
    // Also note that we handle top level pointers here.

    if ( pChild->IsPointer()  &&  ((CG_POINTER *)pChild)->IsRef() )
        {
        // In args would stay in the buffer,
        // out args would be on the Os stack.

        CG_NDR * pPointee = (CG_NDR *)pChild->GetChild();

        if ( ( pChild->GetCGID() == ID_CG_PTR  ||
               pChild->GetCGID() == ID_CG_STRING_PTR  || 
               pChild->GetCGID() == ID_CG_SIZE_PTR )
             &&
             pPointee->IsSimpleType()
             &&
             ( pPointee->GetCGID() != ID_CG_ENUM  ||
               pPointee->GetCGID() == ID_CG_ENUM  &&
                                      ((CG_ENUM*)pPointee)->IsEnumLong() )
             &&
             pPointee->GetCGID() != ID_CG_INT3264
           )
            return;
        }

    pAna->SetRpcSSAllocateRecommended( 1 );
}



/****************************************************************************
 	Implementation of the return type node.
 ****************************************************************************/
CG_STATUS
CG_RETURN::MarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform the size analysis for the return type.

 Arguments:
	
	pAna	- A pointer to the analysis block.

 Return Value:
	
 Notes:

	For the return type, the size analysis contributes nothing on the
	client side, yet we must declare a local variable for the return.
----------------------------------------------------------------------------*/
{

	node_skl			*	pType				= GetType();
	PNAME	   				pName				= RETURN_VALUE_VAR_NAME;
	CG_STATUS				Status;
	CG_NDR				*	pC	= (CG_NDR *)GetChild();

	//
	// Always allocate a local resource for the return type.
	//

	if( (pC->GetCGID() == ID_CG_CONTEXT_HDL ) ||
		(pC->GetChild() && (pC->GetChild()->GetCGID() == ID_CG_CONTEXT_HDL)))
		{
		node_skl	*	pActualType = pType->GetBasicType();
		pActualType	= MakeIDNode( pName, new node_def ("NDR_SCONTEXT" ) );
		SetResource( pAna->AddLocalResource( pName, pActualType ));
		}
	else
		{
		pType	= MakeIDNode( pName, pType );
		SetResource( pAna->AddLocalResource( pName,  pType ));
		}


	SetSizeResource( 0 );

	// Reset the analysis block for marshalling.

	pAna->SetMemoryAllocDone();
	pAna->ResetRefAllocDone();
	pAna->ResetEmbeddingLevel();
	pAna->SetRefChainIntact();
	pAna->SetDontReUseBuffer();
	
	pAna->SetReturnContext();
	pAna->SetLastPlaceholderClass( (CG_NDR *)this );

	// Send the analysis message to the child nodes.

	Status	=  ((CG_NDR *)GetChild())->MarshallAnalysis( pAna );


	SetSizeExpression(
         new expr_constant( 0L, VALUE_TYPE_NUMERIC_U ) );
	
	pAna->ResetReturnContext();

	if( pAna->GetOptimOption() & OPTIMIZE_SIZE )
		{
		pAna->ClearTransientResourceDict();
		}

	return Status;
}

CG_STATUS
CG_RETURN::UnMarshallAnalysis(
	ANALYSIS_INFO	*	pAna )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Perform the buffer analysis for the return type.

 Arguments:
	
	pAna	- A pointer to the analysis block.

 Return Value:
	
 Notes:

	For the return type, the size analysis contributes nothing on the
	client side, yet we must declare a local variable for the return.
----------------------------------------------------------------------------*/
{
	node_skl	*	pType	= GetType();
	PNAME			pName	= RETURN_VALUE_VAR_NAME;
	CG_STATUS		Status;

	//
	// Always allocate a local resource for the return type.
	//

	pType	= MakeIDNode( pName, pType );
	SetResource( pAna->AddLocalResource( pName,  pType ));

	pAna->SetMemoryAllocDone();
	pAna->ResetRefAllocDone();
	pAna->ResetEmbeddingLevel();
	pAna->SetRefChainIntact();
	pAna->SetDontReUseBuffer();
	
	pAna->SetReturnContext();
	pAna->SetLastPlaceholderClass( (CG_NDR *)this );

	Status	= ((CG_NDR *)GetChild())->UnMarshallAnalysis( pAna );

	pAna->ResetReturnContext();

	if( pAna->GetOptimOption() & OPTIMIZE_SIZE )
		{
		pAna->ClearTransientResourceDict();
		}

	return Status;
}