/***
*impmgr.cxx - Import Manager
*
*  Copyright (C) 1991, Microsoft Corporation.  All Rights Reserved.
*  Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
*   The Import Manager for a Type manages references to other Types.
*
*Owner:
*   AlanC
*
*Implementation Notes:
*   The IMPTYPE entries are represented using two parallel arrays.
*   Qimptype(himptype) returns a pointer to the entry in the IMPTYPE
*   array for that import entry.
*   Qubimptype(himptype) returns a pointer to the entry in the UB_IMPTYPE
*   array for the import entry.
*   When an impory entry's reference count goes to zero it is added to
*   the free list.  This implies that the arrays are sparse in that free
*   array elements may be inter-mixed with allocated elements.
*   When a new import entry must be allocated and the free list is empty
*   then cimptypeGrow entries are added to the free list.
*
*   The import manager distinguishes between four types of references
*   to TypeInfos:
*       - a self-referential import entry references the module itself
*         This case is explicitly checked for by testing whether the TypeInfo
*         pointer associated with the entry is equal to the pointer to
*         the TypeInfo which contains the import manager
*       - references to nested TypeInfos (record TypeInfos in same module)
*         The import entries which reference record TypeInfos in this module
*         do not 'bump' the reference count of the record TypeInfos in any way
*         Such references are indicated by m_depkind == DEP_Nested
*       - references to other TypeInfos in the same library.
*         Import entries of this type maintain an internal reference to the
*         TypeInfo
*         Such references are indicated by m_isInternalRef == TRUE
*       - references to TypeInfos in other libraries
*         These maintain a normal external reference.
*
*   Note that references to record TypeInfos defined in other modules are not
*   distinguished from references to other TypeInfos except for the case
*   where an external reference to the record TypeInfo is needed because
*   it is defined by a TypeInfo contained in the same library as this
*   TypeInfo.  It is necessary to explicitly test for this situation in
*   order to add an internal reference to the RecTypeInfo since there is no
*   common protocol that introduces the AddInternal method.
*
* UNDONE: When the impmgr is saved or when it drops a reference to a
* ITypeInfo then it should update its copy of the ITypeInfo's TYPEID
*
*
*Revision History:
*   26-Aug-91 ilanc:  Ripped asserts in Init
*   27-Nov-91 ilanc:  Ripped const cast away of this in
*            DebCheckState and DebShowState.
*   03-Apr-92 martinc: added explicit casts from bitfield to enum (for cfront)
*   18-Jun-92 w-peterh: changes to debshowstate
*   10-Jul-92 w-peterh: added SetTypeInfoAndDepKind()
*   26-Aug-92 rajivk  : wrapped all the DePrintf with ID_TEST and ID_DEBUG
*   17-Sep-92 rajivk:	added functions for editing modules. Edit & Continue
*   15-Nov-92 rajivk:	CheckLaoutDep verifies the project id.
*   12-Feb-93 w-peterh: GetContainingTypeLib returns HRESULT
*   12-Mar-93 rajivk:	Support for Byte swapping.
*
*
*****************************************************************************/

#include "silver.hxx"
#include "typelib.hxx"
#include <new.h>
#include "sheapmgr.hxx"
#include "impmgr.hxx"
#include "xstring.h"
#include "gdtinfo.hxx"
#include "clutil.hxx"
#include "nammgr.hxx"
#include "tip.hxx"
#include "tfixups.hxx"
#include "exbind.hxx"	    // for EXBIND -- BindTypeDefn needs it.

//#if !OE_REALMODE
//#include "genproj.hxx"
//#endif

#include <stdlib.h>

#pragma hdrstop(RTPCHNAME)

#if ID_DEBUG
#undef SZ_FILE_NAME
static char szImpmgrCxx[] = __FILE__;
#define SZ_FILE_NAME szImpmgrCxx
#endif 

//this is a table used to map runtime function ordinals to the address of the function
//on the Mac.

// Define static class constants
#pragma code_seg(CS_STATIC_INIT)
BYTE IMPMGR::bFirstSerByte = 0;
BYTE IMPMGR::bCurVersion = 0;
#define cimptypeGrow 1
#pragma code_seg(CS_STATIC_INIT)


/***
*PUBLIC IMPMGR constructor
*Purpose:
*    need to initialize const/static class members
*     since cfront is buggy and we have no ctor linker.
*
*
*Entry:
*   None.
*
*Exit:
*   None.
*
***********************************************************************/
#pragma code_seg(CS_INIT)
IMPMGR::IMPMGR()
{
    if (bFirstSerByte == 0) {
      bFirstSerByte = 'I' * 2 + 'M';
    }

    m_fCheckLayoutDepCalled    = TRUE;
    m_fCheckRemainingDepCalled = TRUE;
    m_isProjectIdVerified      = TRUE;

    m_lPosOfDeps = 0;
}
#pragma code_seg()

/***
*PUBLIC IMPMGR::Init - import manager initialization method
*Purpose:
*   initialize import manager.
*
*Entry:
*   psheapmgr - pointer to SHEAP_MGR.
*   pbdTimptype - pointer to BLK_DESC of Timptype table
*
*Exit:
*   TIPERROR
*
***********************************************************************/
#pragma code_seg( CS_CORE2 )
TIPERROR IMPMGR::Init(SHEAP_MGR * psheapmgr,
                      BLK_DESC * pbdTimptype,
                      BLK_DESC * pbdTimpaddr,
                      DYN_TYPEROOT *pdtroot)
{
    UINT i;
    TIPERROR err;

    IfErrRet( m_bdTubimptype.Init(psheapmgr, 0) );

    // Save pointer to bdTimptype and bdTimpaddr
    m_pbdTimptype = pbdTimptype;

    DebAssert(pbdTimptype->CbSize() == 0, "IMPMGR::Init bdTimptype not empty");
    DebAssert(pbdTimptype->IsValid(), "IMPMGR::Init bdTimptype not valid");

    // Initialize the bucket table
    for (i = 0; i < IMPMGR_cBuckets; i++) {
      m_rghimptypeBucket[i] = (sHIMPTYPE) HIMPTYPE_Nil;
    }

    m_himptypeFreeList = (sHIMPTYPE) HIMPTYPE_Nil;


    m_fCheckLayoutDepCalled    = TRUE;
    m_fCheckRemainingDepCalled = TRUE;
    m_isProjectIdVerified      = TRUE;
    m_lPosOfDeps = 0;
#if OE_RISC
    m_ptlbtempl = NULL;
#endif 

    m_pdtroot = pdtroot;

    // Note: the destructor checks to see if the bmData block is valid to
    // determine whether or not initialization succeeded
    IfErrRet(m_bmData.Init(psheapmgr));

    return TIPERR_None;
}
#pragma code_seg( )


/***
*PRIVATE IMPMGR::Rqimptype - returns pointer to timptype
*Purpose:
*   Returns pointer to array of pointers to TypeInfo's
*
*Entry:
*   none
*
*Exit:
*   return pointer to timptype
*
***********************************************************************/

inline IMPTYPE* IMPMGR::Rqimptype() const
{
    return (IMPTYPE *)(m_pbdTimptype->QtrOfBlock());
}


/***
*PRIVATE IMPMGR::Rqubimptype - returns pointer to tubimptype
*Purpose:
*   Returns pointer to tubimptype
*
*Entry:
*   none
*
*Exit:
*   return pointer to tubimptype
*
***********************************************************************/

inline UB_IMPTYPE *IMPMGR::Rqubimptype() const
{
    return (UB_IMPTYPE *)(m_bdTubimptype.QtrOfBlock());
}


/***
*PRIVATE IMPMGR::Himptype - return handle for import entry given qubimptype
*Purpose:
*   return handle for import entry given qubimptype
*   Causes no heap movement.
*
*Entry:
*   qubimptype - pointer to UB_IMPTYPE entry
*
*Exit:
*   return handle for import entry
*
***********************************************************************/

inline HIMPTYPE IMPMGR::Himptype(UB_IMPTYPE *qubimptype) const
{
    return (qubimptype - Rqubimptype()) * sizeof(IMPTYPE);
}




/***
*PRIVATE IMPMGR::ReleasePtinfo
*Purpose:
*   Release the typeinfo pointer owned by an import entry.
*   If the pointer is null on entry do nothing.
*   Sets the pointer to null before exiting.
*
*Entry:
*   himptype - handle of import entry whose ptinfo is to be released
*
*Exit:
*   None
***********************************************************************/

#pragma code_seg(CS_CORE2)
VOID IMPMGR::ReleasePtinfo(HIMPTYPE himptype)
{
    ITypeInfoA *ptinfo = ((ITypeInfoA *)(ULONG) Qimptype(himptype)->m_ptinfo);

    // If the pointer is NULL of if the import entry references the
    // container TypeInfo then don't release it
    // 21-Aug-92 ilanc: don't do this for nested types (was == Nested)
    // 24-Aug-92 ilanc: do this for nested types --
    //		   but make it internal release.
    // 24-Aug-92 ilanc: if this is nested type contained w/in
    //			 this type -- do nothing as well.
    // 24-Aug-92 ilanc: commented out DEP_Nested conjunct.
    // 25-Aug-92 ilanc: reintroduced DEP_Nested conjunct since
    //			 it's only ever set for nested types
    //			 of this module and SetNestedTypeInfo
    //			 doesn't an int/ext refcount.
    //
    if (ptinfo != NULL &&
	ptinfo != m_pdtroot->Pgdtinfo()) {

      if (Qubimptype(himptype)->m_depkind != DEP_Nested) {

        // If this is a reference to a TypeInfo in the same library then
        // release its internal reference; otherwise do a normal release
	if (Qubimptype(himptype)->m_isInternalRef) {
	  // Note that we know the truetype of the typeinfo in this case
	  // since ptinfo must point into this impmgr's containing typelib.
	  // That truetype supports the ole implementation of STL_TYPEINFO
	  // even though we can't QueryInterface to STL_TYPEINFO.
	  ((STL_TYPEINFO *)ptinfo)->RelInternalRef();
	}
        else
          ptinfo->Release();
      }
      Qimptype(himptype)->m_ptinfo = NULL;
    }
}
#pragma code_seg()


/***
*PRIVATE IMPMGR::SetPtinfo
*Purpose:
*   Set m_ptinfo of the import entry to the passed in TypeInfo
*   SetPtinfo changes the internal/external reference status of the
*   passed in TypeInfo as needed to account for the addition of
*   a reference to that TypeInfo from the import entry.
*
*Entry:
*   himptype - handle of import entry whose ptinfo is to be released
*   ptinfo - pointer to TypeInfo
*
*Exit:
*   None
***********************************************************************/

#pragma code_seg( CS_COMPILE )
TIPERROR IMPMGR::SetPtinfo(HIMPTYPE himptype, ITypeInfoA *ptinfo, BOOL fDepBeingRead)
{
    TIPERROR err;
    ITypeLibA *ptlib1, *ptlib2;


    // Store the reference
    Qimptype(himptype)->m_ptinfo = ptinfo;

    // Avoid adding a reference to the typeinfo which contains
    // the import manager so that there is not a circular reference problem
    if (ptinfo == m_pdtroot->Pgdtinfo())
      return TIPERR_None;

    // Assume that an external reference will be needed.
    ptinfo->AddRef();

    // Check whether the referenced TypeInfo is in the same library.
    // If it is then release the external reference and add an internal
    // reference to the TypeInfo instead.
    // Have to deal with the REC_TYPEINFO and STL_TYPEINFO cases separately
    // since there is no protocol that contains the AddInternalRef function

    // First get a pointer to the containing TypeLib
    err = TiperrOfHresult(m_pdtroot->Pgdtinfo()->GetContainingTypeLib(&ptlib2, NULL));
    DebAssert(err == TIPERR_None, "SetPtinfo - no container");
    ptlib2->Release();

    // If ptinfo is contained in the same library as this impmgr's typeinfo,
    // then change the reference to internal.
    err = TiperrOfHresult(ptinfo->GetContainingTypeLib(&ptlib1, NULL));
    DebAssert(err == TIPERR_None, "SetPtinfo - no container");

    ptlib1->Release();
    if (ptlib1 == ptlib2) {
      ((STL_TYPEINFO *)ptinfo)->AddInternalRef();
      ptinfo->Release();
      Qubimptype(himptype)->m_isInternalRef = TRUE;
    }
    return TIPERR_None;
}
#pragma code_seg( )


/***
*PRIVATE IMPMGR::HimptypeAlloc
*Purpose:
*   Allocate an imptype entry.  Grows free list if its empty.
*
*Entry:
*   phimptype - Return parameter for himptype
*
*Exit:
*   himptype - handle for allocated import entry
*
*CONSIDER: it may not be worth the extra code to grow the free list
*CONSIDER: 10 at a time since the SHEAPMGR will be changed to buffer
*CONSIDER: the heap entries
***********************************************************************/

#pragma code_seg( CS_COMPILE )
TIPERROR IMPMGR::HimptypeAlloc(sHIMPTYPE *phimptype)
{
    UB_IMPTYPE *qubimptype, *qubimptypeEmpty;
    IMPTYPE *qimptype;
    UINT cbSize;
    HIMPTYPE himptype;
    TIPERROR err;

    if (m_himptypeFreeList == (sHIMPTYPE) HIMPTYPE_Nil) {

      // Add cimptypeGrow Imptype enties to free list

      // Increase size of Timptype array by cimptype entries
      // and initialize each entry to NULL
      // Note that the constructor of the IMPTYPE entries is never called
      cbSize = m_pbdTimptype->CbSize();
      IfErrRet( m_pbdTimptype->Realloc(cbSize +
                                       cimptypeGrow*sizeof(IMPTYPE)) );
      qimptype = (IMPTYPE *)(((BYTE *)Rqimptype()) + cbSize);
      if (cimptypeGrow == 1)
        ::new (qimptype) IMPTYPE;
      else
      ::new (qimptype) IMPTYPE[cimptypeGrow];

      // Grow the Tubimptype array by cimptypeGrow elements
      cbSize = m_bdTubimptype.CbSize();

      if (err = m_bdTubimptype.Realloc(cbSize +
                                       cimptypeGrow*sizeof(UB_IMPTYPE)) ) {
        // Undo growing the Timptype table
        m_pbdTimptype->Realloc(m_pbdTimptype->CbSize() -
                               cimptypeGrow*sizeof(IMPTYPE));
        return err;
      }

      // Initialize entries and link them into the free list
      // qubimptypeEmpty points to front of free list when loop exited
      qubimptypeEmpty = (UB_IMPTYPE *)(((BYTE *)Rqubimptype()) + cbSize);
      himptype = m_himptypeFreeList;
      for (qubimptype = qubimptypeEmpty + cimptypeGrow - 1;
             qubimptype >= qubimptypeEmpty;
             qubimptype--) {

	// call the Constructor to initialize the UB_IMPTYPE instance.
        ::new (qubimptype) UB_IMPTYPE;

	qubimptype->m_himptypeNext = (sHIMPTYPE) himptype;

	himptype = Himptype(qubimptype);
      }

    }
    else {
      qubimptypeEmpty = Qubimptype((HIMPTYPE) m_himptypeFreeList);
      DebAssert(qubimptypeEmpty->m_cRefs == 0 &&
		 qubimptypeEmpty->m_isInternalRef == FALSE &&
		 qubimptypeEmpty->m_isExcodeRef == FALSE &&
		 qubimptypeEmpty->m_isDeclRef == FALSE &&
		 qubimptypeEmpty->m_refkind == REF_NoName &&
		 qubimptypeEmpty->m_depkind == DEP_None, " The way things should be ");
    }


#if ID_DEBUG
    // Set up head of free list and check state of the IMPMGR
    m_himptypeFreeList = (sHIMPTYPE) Himptype(qubimptypeEmpty);
    DebCheckState(1);
#endif 

    // qubimptypeEmpty points to the head of the free list
    // Update the m_himptypeFreeList to point to the second element
    // in the free list and return the old head.
    m_himptypeFreeList = (sHIMPTYPE) qubimptypeEmpty->m_himptypeNext;

    // Reset the NEXT pointer of the entry we're returning
    qubimptypeEmpty->m_himptypeNext = (sHIMPTYPE) HIMPTYPE_Nil;

    *phimptype = Himptype(qubimptypeEmpty);
    return TIPERR_None;
}
#pragma code_seg( )


/***
*PRIVATE IMPMGR::NewEntry - allocate a new IMPTYPE entry
*Purpose:
*   Allocates and initializes a new IMPTYPE entry.
*   Links the new entry into the front of the bucket list.
*
*Entry:
*   pqubimptype - returns pointer to UB_IMPTYPE of the new entry
*   phimptype - returns the himptype of the new entry
*
*Exit:
*   TIPERROR
*
***********************************************************************/
#pragma code_seg( CS_COMPILE )
TIPERROR IMPMGR::NewEntry(UB_IMPTYPE **pqubimptype,
			  sHIMPTYPE *phimptype)
{
    TIPERROR err;
    UB_IMPTYPE *qubimptype;

    IfErrRet( HimptypeAlloc(phimptype) );

    // Initialize the new entry
    Qimptype(*phimptype)->m_ptinfo = NULL;
    qubimptype = *pqubimptype = Qubimptype(*phimptype);

    // Following assertions are true because the entry was previously free
    DebAssert( qubimptype->m_cRefs == 0 &&
	       qubimptype->m_isExcodeRef == FALSE,
                        "IMPMGR::NewEntry: entry incorrectly initialized");

    qubimptype->m_refkind = REF_Name; // Set to this so that the entry
                                      // can be passed to Free without
                                      // additional initialization
    qubimptype->m_depkind = DEP_None;

    return TIPERR_None;
}
#pragma code_seg( )


/***
*PRIVATE IBucket - hash function for hlnam
*Purpose:
*   Map hlnam to a bucket index.
*Entry:
*   hlnam
*
*Exit:
*   bucket index
*
***********************************************************************/

inline UINT IMPMGR::IBucket(HLNAM hlnam)
{
    return (hlnam >> 3) % IMPMGR_cBuckets;
}


/***
*PRIVATE IMPMGR::IBucket - hash function for qualified name
*Purpose:
*   Map a qualified name to the index of the bucket in which an import
*   entry with that name should be stored.
*
*Entry:
*   cName - number of names in qualified name
*   rghlnam - array of local name handles
*
*Exit:
*   bucket index
*
***********************************************************************/

inline UINT IMPMGR::IBucket(UINT cName, sHLNAM rghlnam[])
{
    // Hash based on the last name
    return IBucket(rghlnam[cName - 1]);
}







/***
*PUBLIC IMPMGR::GetHimptype - return Himptype given pointer to TypeInfo
*Purpose:
*   Returns handle of ImpType entry associated with passed in TypeInfo.
*   Adds new entry if there is none and assigns it depkind.
*   Always increments the reference count of the import entry.
*
*Entry:
*   ptinfo - pointer to TypeInfo
*   depkind - specifies to what level decompilation is needed if can't
*             refind the TypeInfo.
*   phimptype -  parameter for returning HIMPTYPE
*
*Exit:
*   TIPERROR
*
***********************************************************************/

#pragma code_seg( CS_COMPILE )
TIPERROR IMPMGR::GetHimptype(ITypeInfoA *ptinfo,
                             DEPEND_KIND depkind,
			     sHIMPTYPE *phimptype)
{
    UB_IMPTYPE *qubimptype;
    TIPERROR err;
    UINT cImpType = m_pbdTimptype->CbSize() / sizeof(IMPTYPE);
    UB_IMPTYPE *rqubimptype = Rqubimptype();
    IMPTYPE *rqimptype = Rqimptype();
    BOOL  fNew = FALSE;

    *phimptype = GetHimptypeIfExists( ptinfo );
    if ( *phimptype != HIMPTYPE_Nil) {
      qubimptype = Qubimptype(*phimptype);
    }
    else {
      // No matching entry found --- allocate a new one
      // Note that the new entry is not linked into the hash table
      IfErrRet (NewEntry(&qubimptype, phimptype));

      qubimptype->m_refkind = (depkind == DEP_Base) ? REF_Base :
						      REF_NoName;
      fNew = TRUE;
    }

    // Set the depend kind to MAX_OF(qubimptype->m_depkind and depkind)
    qubimptype->m_depkind = max(qubimptype->m_depkind, depkind);
    qubimptype->m_cRefs++;

    if (fNew)
      IfErrRet(SetPtinfo(*phimptype, ptinfo));

    return TIPERR_None;
}
#pragma code_seg( )




/***
*PUBLIC IMPMGR::GetHimptypeIfExists - return Himptype, given a pointer to TypeInfo
*Purpose:
*	It returns the corresponding himptype for given typeinfo.
*	If there is no import entry for this typeinfo then HIMPTYPE_Nil
*	is returned.
*
*Entry:
*   ptinfo - pointer to TypeInfo
*
*
*Exit:
*   HIMPTYPE
*
***********************************************************************/

#pragma code_seg( CS_CORE2 )
HIMPTYPE IMPMGR::GetHimptypeIfExists(ITypeInfoA *ptinfo)
{
    UINT	    i;
    UINT	    cImpType = m_pbdTimptype->CbSize() / sizeof(IMPTYPE);
    UB_IMPTYPE	    *rqubimptype = Rqubimptype();
    IMPTYPE	    *rqimptype = Rqimptype();

    // Does a linear search on ImpAddr to find a matching TypeInfo pointer.
    // CONSIDER: building a non-persistant hash table to make this faster
    for (i = 0; i < cImpType; i++)
      if (!rqubimptype[i].isFree() &&         // if not a free entry
          rqimptype[i].m_ptinfo == ptinfo) {  // and TypeInfo ptr matches

	return HimptypeOfIndex(i);
      }

    return HIMPTYPE_Nil;

}
#pragma code_seg( )



#if ID_DEBUG
/***
*PUBLIC GetName
*Purpose:
*   Retrieves the array of names for an ImpType entry.  The
*   returned pointer may become invalid after invoking other
*   methods on the import manager.
*
*Entry:
*   himptype - handle for import entry whose name is to be retrieved
*   pcName - returns the number of names
*   prghlnam - returns pointer to array of names
*
*Exit:
*   TIPERROR
*
***********************************************************************/

VOID IMPMGR::GetName(HIMPTYPE himptype, UINT *pcName, sHLNAM *prghlnam[])
{
    UB_IMPTYPE *qubimptype = Qubimptype(himptype);
    USHORT *qus;

    if (qubimptype->m_refkind == REF_Name) {
      *pcName = 1;
      *prghlnam = &(qubimptype->m_hlnam);
    }
    else {
      DebAssert(qubimptype->m_refkind == REF_QualName, "");
      qus = (USHORT *)m_bmData.QtrOfHandle(qubimptype->m_hrghlnam);
      *pcName = *qus;
      *prghlnam = (sHLNAM *)(qus + 1);
    }
}
#endif 




/***
*PUBLIC IMPMGR::GetTypeInfo - returns pointer to TypeInfo of import entry
*Purpose:
*   Returns pointer to TYPEINFO of import entry.
*
*Entry:
*   himptype - handle for ImpType entry
*   pptinfo - returns pointer to ITypeInfo
*
*Exit:
*   TIPERROR
*   If TIPERROR = TIPERR_None then *pptinfo returns a pointer to the
*   TYPEINFO.  This reference must be released by the caller.
*
***********************************************************************/

#pragma code_seg( CS_COMPILE )
TIPERROR IMPMGR::GetTypeInfo(HIMPTYPE himptype,
                             DEPEND_KIND depkind,
			     ITypeInfoA **pptinfo)
{
    TIPERROR err;

    // We don't care what depkind is set to by this call.
    IfErrRet( CheckRemainingDep( &depkind ) );
    *pptinfo = Qimptype(himptype)->m_ptinfo;
    DebAssert(*pptinfo != NULL, "IMPMGR::GetTypeInfo: m_ptinfo null");

    // Add a reference to account for the TypeInfo pointer being returned
    (*pptinfo)->AddRef();
    return TIPERR_None;
}
#pragma code_seg( )




/***
*PUBLIC IMPMGR::AddrefHimptype - add reference to an import entry
*Purpose:
*   Increments reference count of import entry
*
*Entry:
*   himptype - handle for ImpType entry
*
*Exit:
*   none
*
***********************************************************************/

#pragma code_seg( CS_COMPILE )
void IMPMGR::AddrefHimptype(HIMPTYPE himptype)
{
    UB_IMPTYPE *qubimptype;

    if (himptype==HIMPTYPE_Nil) return;

    qubimptype = Qubimptype(himptype);
    qubimptype->m_cRefs++;
}
#pragma code_seg( )


/***
*PUBLIC IMPMGR::Unref - unreference an import entry
*Purpose:
*   Decrements reference count of import entry
*
*Entry:
*   himptype - handle for ImpType entry
*
*Exit:
*   none
*
***********************************************************************/

#pragma code_seg( CS_COMPILE )
void IMPMGR::Unref(HIMPTYPE himptype)
{
    UB_IMPTYPE *qubimptype;

    if (himptype==HIMPTYPE_Nil) return;

    qubimptype = Qubimptype(himptype);

    DebAssert(qubimptype->m_cRefs > 0, "IMPMGR::Unref: m_cRefs <= 0");
    
    qubimptype->m_cRefs--;

    // Assert that if the ref count is zero and the entry is not
    // going to be freed, then the NEXT field does not contain a 
    // valid HIMPTYPE.
    //
    DebAssert( qubimptype->m_cRefs != 0
               || qubimptype->isFree()
               || qubimptype->m_himptypeNext == (sHIMPTYPE)HIMPTYPE_Nil,
	      "Next field is not Nil");

    DebAssert (!(Qubimptype(himptype)->isFree()), "typelib import entries aren't ever freed");
}
#pragma code_seg( )


/***
*PUBLIC IMPMGR::HimptypeFirst - return first Himptype in sequence
*Purpose:
*   Returns first Himptype in sequence.
*   Subsequent elements of the sequence are generated by HimptypeNext
*   Used by clients who need to iterate over all valid Himptype's
*
*Entry:
*   None
*
*Exit:
*   return first Himptype or HIMPTYPE_Nil if no Himptype's
*
***********************************************************************/

#pragma code_seg( CS_CORE2 )
HIMPTYPE IMPMGR::HimptypeFirst() const
{
    // The above becomes greatly simplified in the OLE case, where
    // import entries are never freed (isFree() always returns FALSE).
    // I chose to do this by hand instead of counting on the C optimizer,
    // because this routine is called a lot.
    if (m_pbdTimptype->CbSize()) {
      return 0;
    }
    return HIMPTYPE_Nil;
}
#pragma code_seg( )


/***
*PUBLIC IMPMGR::HimptypeNext - return next Himptype in sequence
*Purpose:
*   Return the Himptype in the sequence which follows HimptypeNext.
*   Note that the ordering of elements in the sequence is irrelevant as
*   long as all Himptypes are in the sequence exactly once.
*   Used by clients who need to iterate over all valid Himptype's
*
*Entry:
*   Himptype
*
*Exit:
*   return next Himptype in sequence or HIMPTYPE_Nil if no more elements
*   in the sequence
*
***********************************************************************/

#pragma code_seg( CS_COMPILE )
HIMPTYPE IMPMGR::HimptypeNext(HIMPTYPE himptype) const
{
    // The above becomes greatly simplified in the OLE case, where
    // import entries are never freed (isFree() always returns FALSE).
    // I chose to do this by hand instead of counting on the C optimizer,
    // because this routine is called a lot.
    if ((himptype+sizeof(IMPTYPE)) < m_pbdTimptype->CbSize()) {
      return himptype+sizeof(IMPTYPE);
    }
    return HIMPTYPE_Nil;
}
#pragma code_seg( )






//
// Import Address Support
//


/***
*PUBLIC IMPMGR ::RegisterDeclRefDep
*Purpose: Registers a dependency of the typeinfo (of this impmgr) on Ptinfo.
*	  The dependency registered is of type declref.
*
*Entry:
*	ptinfo : typeinfo on which this typeinfo depends on.
*
*Exit:
*   TIPERROR
*
*
***********************************************************************/
TIPERROR IMPMGR::RegisterDeclRefDep(ITypeInfoA *ptinfo)
{
    TIPERROR err=TIPERR_None;
    sHIMPTYPE himptype;
    DEPEND_KIND depkind;

    depkind = m_pdtroot->CompState() < CS_DECLARED ? DEP_Layout : DEP_Code;

    IfErrRet(GetHimptype(ptinfo, depkind, &himptype));

    // Set the DeclRef flag and unref the
    // himptype to decrement the reference added by GetHimptype.

    Qubimptype(himptype)->m_isDeclRef = TRUE;
    Unref(himptype);

    DebAssert(himptype != HIMPTYPE_Nil, " Bad Himptype ");

    return err;
}



/***
*PUBLIC IMPMGR::CheckLayoutDep - read import typeids which has the
*		dependncy of the kind DEP_Layout.
*Purpose:
*   Read previously serialized Typeids from stream.
*   Does modify Timptype table.
*
*Entry:
*
*Exit:
*   TIPERROR
*    Note:- if TIPERR_ElementNotFound error is returned  then the caller
*	    needs to decompile the module.
*
*
***********************************************************************/

#pragma code_seg( CS_CORE2 )
TIPERROR IMPMGR::CheckLayoutDep()
{
    TIPERROR	      err = TIPERR_None;



    return err;
}
#pragma code_seg( )



/***
*PUBLIC IMPMGR::CheckRemainingDep - read import typeids for
*		dependncy that are not of DEP_KIND (DEP_Layout).
*Purpose:
*   Read previously serialized Typeids from stream.
*   Does modify Timptype table.
*
*Entry:
*     pdepkindFail: used for return value. Represents the dependency type that
*		    failed.
*
*Exit:
*   TIPERROR
*
***********************************************************************/
#pragma code_seg( CS_CORE2 )
TIPERROR IMPMGR::CheckRemainingDep(DEPEND_KIND *pdepkindFail)
{
    TIPERROR	      err = TIPERR_None;

    BYTE	      b;
    UINT	      cEntries;
    UB_IMPTYPE	      *qubimptype;
    IMPTYPE	      *qimptype, *qimptypeLimit;
    LPSTR	      szTypeId=NULL;
    ITypeInfoA	      *ptinfo;
    HIMPTYPE	      himptype;
    STREAM	      *pstrm;
    GenericTypeLibOLE *pgtlibole;

    *pdepkindFail = DEP_None;
    if ( m_fCheckRemainingDepCalled )

	return TIPERR_None;



    // open the stream
    IfErrRet(m_pdtroot->Pgdtinfo()->OpenStream(&pstrm, SOM_Read));

    // Set the flag indicating that this function has been called.
    m_fCheckRemainingDepCalled = TRUE;

    // set the position of the head to read the typeinfos.
    IfErrGo(pstrm->SetPos( m_lPosOfDeps ));

    // Lock the block desc so that the memory does not move
    m_bdTubimptype.Lock();
    m_pbdTimptype->Lock();


    qimptype = Rqimptype();
    qubimptype = Rqubimptype();

    // Read in the TypeId associated with each TypeInfo when the IMPMGR
    // was saved.  Convert it to a pointer to the TypeInfo.
    cEntries = m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE);

    pgtlibole = m_pdtroot->Pgdtinfo()->PgtlibOleContaining();

    qimptypeLimit = Rqimptype() + cEntries;
    for (; qimptype < qimptypeLimit; qimptype++, qubimptype++) {
      if ( qubimptype->isFree() == FALSE ) {

	// Read in the typeid.
	IfErrGo(pstrm->ReadSz(&szTypeId));

	// Map TypeId to the associated TypeInfo
	err = pgtlibole->TypeInfoFromCompressedTypeId(szTypeId, &ptinfo);

	// Free the szTypeId
	MemFree(szTypeId);
	szTypeId = NULL;

	IfErrGo(err);

	// Convert the qimptype to an Himptype
	himptype = (BYTE *)qimptype - m_pbdTimptype->QtrOfBlock();

	err = SetPtinfo(himptype, (TYPEINFO *)ptinfo);
	// fall through ...

	// Release the reference added by the TypeInfoFromCompressedTypeId
	ptinfo->Release();

	IfErrGo(err);
      }   // if
    }	  // for

    // Ensure that we have read in all the TypeIds
    IfErrGo( pstrm->ReadByte(&b) );

    if (b != bFirstSerByte) {
      err = TIPERR_InvDataRead;
      goto Error;
    }

Error:
    // UnLock the block desc(s)
    m_bdTubimptype.Unlock();
    m_pbdTimptype->Unlock();

    if (szTypeId)
      MemFree(szTypeId);

    // If there was an error then we want to flag that remaining dep(s)
    // were not read in.
    if (err)  {
       // Set the flag indicating that this function has not been called.
       m_fCheckRemainingDepCalled = FALSE;
    }
    else {
      // Set the himptype levels.
      err = m_pdtroot->MakeHimptypeLevels();
    }

    // Release the STREAM;
    pstrm->Release();

#if ID_DEBUG
    // Check the state
    if ((!err) && (*pdepkindFail == DEP_None))
      DebCheckState(1);
#endif 

    return err;
}
#pragma code_seg( )



/***
*PUBLIC IMPMGR::Read - read import manager from stream
*Purpose:
*   Read previously serialized IMPMGR from stream.
*   Does not modify Timptype table.
*
*Entry:
*   pstrm - stream from which IMPMGR is read
*
*Exit:
*   TIPERROR
*
***********************************************************************/

#pragma code_seg( CS_LOADPROJ )
TIPERROR IMPMGR::Read(STREAM *pstrm)
{
    BYTE b;
    BYTE bVersion;
    UINT cEntries;
    TIPERROR err;
    IMPTYPE	      *qimptype, *qimptypeLimit;
    HIMPTYPE	      himptype;

    IfErrRet( pstrm->ReadByte(&b) );

    if (b != bFirstSerByte || DebErrorNow(TIPERR_InvDataRead)) {
      return TIPERR_InvDataRead;
    }

    IfErrRet( pstrm->ReadByte(&bVersion) );

    if (bVersion != bCurVersion || DebErrorNow(TIPERR_UnsupFormat)) {
      return TIPERR_UnsupFormat;
    }


    // Read in head of free list and bucket table
    IfErrRet( pstrm->ReadUShort(&m_himptypeFreeList));

    IfErrRet( pstrm->Read(&m_rghimptypeBucket, sizeof(m_rghimptypeBucket)));

#if HP_BIGENDIAN
    // For big endian swap the bytes back after reading
    SwapShortArray(m_rghimptypeBucket, IMPMGR_cBuckets);
#endif 


    IfErrRet( m_bdTubimptype.Read(pstrm) );

#if HP_BIGENDIAN
    SwapStructArray(m_bdTubimptype.QtrOfBlock(),
		    m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE),
		    UB_IMPTYPE_Layout);
#endif 


    IfErrRet( m_bmData.Read(pstrm) );

#if HP_BIGENDIAN
    SwapbmData(TRUE);
#endif 

    cEntries = m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE);
    IfErrRet( m_pbdTimptype->Realloc(cEntries * sizeof(IMPTYPE)) );
    qimptype = Rqimptype();

    qimptypeLimit = Rqimptype() + cEntries;

    for (; qimptype < qimptypeLimit; qimptype++) {
      // Convert the qimptype to an Himptype
      // Initialize each pointer of imptype to NULL;
      himptype = (BYTE *)qimptype - m_pbdTimptype->QtrOfBlock();
      // Initialize to NULL
      Qimptype(himptype)->m_ptinfo = NULL;
    }

    // set to false so that the project id gets verified in CheckLayoutDep
    if(m_pdtroot->CompState() >= CS_DECLARED)
      m_isProjectIdVerified = FALSE;
    else
      m_isProjectIdVerified = TRUE;

    // Initialize the flags indicating that the CheckRemainingDep and
    // CheckLayoutDep has not yet been read.
    m_fCheckRemainingDepCalled = FALSE;


    // save the position to the beginning of the typeids
    pstrm->GetPos( &m_lPosOfDeps );


    return TIPERR_None;
}
#pragma code_seg( )


/***
*PUBLIC IMPMGR::Write - write import manager from stream
*Purpose:
*   Serialize IMPMGR to stream.
*   Does not serialize Timptype since that is separately serialized.
*
*Entry:
*   pstrm - stream to which IMPMGR is written
*
*Exit:
*   TIPERROR
*
***********************************************************************/

#pragma code_seg(CS_OLE_CREATE_OR_SAVEPROJ)
TIPERROR IMPMGR::Write(STREAM *pstrm)
{
    TIPERROR err;
    ULONG ulstlibMajorId;
    USHORT ustlibMinorId;


    // Write out identification byte and version number
    IfErrRet( pstrm->WriteByte(bFirstSerByte) );
    IfErrRet( pstrm->WriteByte(bCurVersion) );


    // Write out head of free list and bucket table
    IfErrRet( pstrm->WriteUShort(m_himptypeFreeList));


#if HP_BIGENDIAN
    // For big endian swap the bytes before writing out and then swap back
    SwapShortArray(m_rghimptypeBucket, IMPMGR_cBuckets);
#endif 

    err = pstrm->Write(m_rghimptypeBucket, sizeof(m_rghimptypeBucket));

#if HP_BIGENDIAN
    // For big endian swap the bytes before writing out and then swap back
    SwapShortArray(m_rghimptypeBucket, (UINT)IMPMGR_cBuckets);
#endif 

    IfErrRet(err);


#if HP_BIGENDIAN
    SwapStructArray(m_bdTubimptype.QtrOfBlock(),
		    m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE),
		    UB_IMPTYPE_Layout);
#endif 

    // Write out tubimptype
    err = m_bdTubimptype.Write(pstrm);

#if HP_BIGENDIAN
    SwapStructArray(m_bdTubimptype.QtrOfBlock(),
		    m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE),
		    UB_IMPTYPE_Layout);
#endif 

    IfErrRet(err);

#if HP_BIGENDIAN
    SwapbmData(FALSE);
#endif 
    err = m_bmData.Write(pstrm);

#if HP_BIGENDIAN
    SwapbmData(TRUE);
#endif 

    IfErrRet(err);


    // Write the project Id so that when the module gets loaded it can verify
    // whether the containing project got changed while the typeinfo was unloaded.
    // NOTE: the verification of Id is done in CheckLayoutDep.

    // Get the current ProjectId
    m_pdtroot->Pgdtinfo()->PgtlibOleContaining()->GetProjectId(&ulstlibMajorId,&ustlibMinorId);

    // For OLE libraries we don't need to verify the project ID and
    // we WriteLayoutEntries is called only once to write out the TypeIds
    // for all referenced TypeInfos.
    // write the type info for which the depend kind is not DEP_Layout kind
    IfErrRet(WriteLayoutEntries(pstrm, FALSE));


    // Write out identification byte in the end so that it is verified in
    // CheckRemainingDep that we are in sync.
    IfErrRet( pstrm->WriteByte(bFirstSerByte));

    return TIPERR_None;

}
#pragma code_seg()


/***
*PUBLIC IMPMGR::WriteLayoutEntries - write import manager's TYPEID to stream
*Purpose:
*	Writes out the typeids.
*Entry:
*   pstrm - stream to which IMPMGR is written
*   isLayoutDep - True if entries for layout dep has to be written
*		  False if the remaining dep has to be written
*
*Exit:
*   TIPERROR
*
***********************************************************************/
#pragma code_seg(CS_OLE_CREATE_OR_SAVEPROJ)
TIPERROR IMPMGR::WriteLayoutEntries( STREAM *pstrm, BOOL isLayoutDep )
{
    TIPERROR err;
    IMPTYPE *qimptype, *qimptypeEnd;
    UB_IMPTYPE *qubimptype;
    GenericTypeLibOLE *pgtlibole;


    pgtlibole = m_pdtroot->Pgdtinfo()->PgtlibOleContaining();

    qubimptype = Rqubimptype();
    qimptype = Rqimptype();
    qimptypeEnd = Rqimptype() + (m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE));

    for(; qimptype < qimptypeEnd; qimptype++, qubimptype++) {
      if ( qubimptype->isFree() == FALSE ) {
	IfErrRet(pgtlibole->WriteTypeId(pstrm, qimptype->m_ptinfo));
      }
    }


    return TIPERR_None;
}
#pragma code_seg()




/***
*PUBLIC IMPMGR::~IMPMGR - IMPMGR destructor
*Purpose:
*   release resources of IMPMGR instance
*Entry:
*   psstrm - stream to which IMPMGR is written
*
*Exit:
*   none
*
***********************************************************************/
#pragma code_seg( CS_TERM )
IMPMGR::~IMPMGR()
{
    HIMPTYPE himptype;

    // Check to ensure that initialization was successful
    if (m_bdTubimptype.IsValid() && m_bmData.IsValid()) {

      // Walk the Timptype array and release every TypeInfo
      for (himptype = HimptypeFirst();
           himptype != HIMPTYPE_Nil;
           himptype = HimptypeNext(himptype))
        ReleasePtinfo(himptype);

      // Shrink space usage back to minimum
      m_pbdTimptype->Realloc(0);

      //the destructor for member m_pbdTubimptype will release its space
    }


}
#pragma code_seg( )


/***
*PUBLIC IMPMGR::RemoveInternalRefs() - Removes internal references.
*Purpose:
*   This function nulls out the pointer to the referenced typeinfo
*   if this reference is to an internal module. This is done so that
*   in case of a cycle there is no dangling pointer reference.
*   This is called from GEN_DTINFO::RemoveInternalRefs();
*
*   WARNING :: This should only be called from GEN_DTINFO::RemoveInternalRefs()
*	       Which is called from STAT_TYPELIBs desctructor. Never call this
*	       function unless you are sure what this does as it will create
*	       leaks if the type infos are not deleted explicitly.
*
*Entry:
*    None.
*
*Exit:
*   none
*
***********************************************************************/
#pragma code_seg( CS_TERM )
VOID IMPMGR::RemoveInternalRefs()
{
    HIMPTYPE	  himptype;

    DebAssert(m_bdTubimptype.IsValid() && m_bmData.IsValid(), "");

      // Walk the Timptype array and release every TypeInfo
    for (himptype = HimptypeFirst();
      himptype != HIMPTYPE_Nil;
      himptype = HimptypeNext(himptype)) {

      if ( Qubimptype(himptype)->m_isInternalRef )
	Qimptype(himptype)->m_ptinfo = NULL;
    }
}
#pragma code_seg( )




#if HP_BIGENDIAN
/***
*PRIVATE IMPMGR::SwapbmData
*Purpose:
*   Swaps the array(s) of sHLNAM(s) stored in the BLK_MGR (m_bmData)
*
*Entry:
*   fSwapFirst == TRUE if we're un-swapping (i.e. we must swap any
*	data first before we look at it)
*
*Exit:
*   None.
*
***********************************************************************/
#pragma code_seg(CS_CORE2)
VOID IMPMGR::SwapbmData(BOOL fSwapFirst) const
{
    UINT	 cEntries;
    UINT	 i;
    UB_IMPTYPE	 *rqubimptype = Rqubimptype();
    sHCHUNK	  hchunk;
    USHORT	 cHlnam;
    HLNAM	 *qhlnam;


    // Count the number of UB_IMPTYPE
    cEntries = m_bdTubimptype.CbSize() / sizeof(UB_IMPTYPE);

    for (i=0; i < cEntries; i++, rqubimptype++) {

      if (rqubimptype->m_refkind == REF_QualName) {
	hchunk = rqubimptype->m_hrghlnam;

	qhlnam = (HLNAM *)m_bmData.QtrOfHandle(hchunk);

	// Get the count of HLNAM(s)
	cHlnam = *(USHORT *)qhlnam;

	if (fSwapFirst)				// if un-swapping, must unswap
	    SwapStruct(&cHlnam, "s");		// this before we use it
						// (otherwise we trash a TON
						// of memory -- trust me :) )

	// Swap the array of SHORT(s) stored at hchunk.
	// +1 is for the count of the HLNAM(s)
	SwapShortArray(qhlnam, cHlnam +1);
      }
    }
}
#pragma code_seg()
#endif 





#if ID_DEBUG
#pragma code_seg(CS_DEBUG)
/***
*PUBLIC IMPMGR::DebCheckState
*Purpose:
*   Check internal state of IMPMGR.
*
*Entry:
*   uLevel
*
*Exit:
*   None.
*
***********************************************************************/

nonvirt void IMPMGR::DebCheckState(UINT uLevel) const
{
    UINT cbTubimptype = m_bdTubimptype.CbSize();
    UINT cbSize = m_pbdTimptype->CbSize();
    UB_IMPTYPE *qubimptype;
    NAMMGR *pnammgr;
    USHORT *qus;
    UINT i;
    HIMPTYPE himptype;
    UINT cEntries = 0;

    //
    // First check the ImpType tables
    //
    DebAssert(cbTubimptype / sizeof(UB_IMPTYPE) ==
                              cbSize / sizeof(IMPTYPE),
                  "IMPMGR::DebCheckState: table sizes inconsistent");
    DebAssert(m_pbdTimptype->IsValid(),
                     "IMPMGR::DebCheckState bdTimptype not valid");
    DebAssert(m_bdTubimptype.IsValid(),
                    "IMPMGR::DebCheckState bdTubimptype not valid");

    DebAssert(m_pdtroot->GetNamMgr(&pnammgr) == TIPERR_None, "");
    for (himptype = HimptypeFirst();
         himptype != HIMPTYPE_Nil;
         himptype = HimptypeNext(himptype)) {
      cEntries++;
      qubimptype = Qubimptype(himptype);
      if ((qubimptype->m_depkind != DEP_None) &&
	  (qubimptype->m_depkind != DEP_Nested) &&
	  (qubimptype->m_refkind != REF_Name) &&
	  (qubimptype->m_refkind != REF_QualName))
        DebAssert(Qimptype(himptype)->m_ptinfo != NULL,
		    "IMPMGR::DebCheckState: Ptinfo should be set. ");

      if (qubimptype->m_refkind == REF_Name)
        pnammgr->DebCheckHlnam(qubimptype->m_hlnam);
      else if (qubimptype->m_refkind == REF_QualName) {
	qus = (USHORT *)m_bmData.QtrOfHandle(qubimptype->m_hrghlnam);
        for (i = *qus++; i > 0; i--)
          pnammgr->DebCheckHlnam(*(sHLNAM *)qus++);
      }
      else
	DebAssert(qubimptype->m_refkind == REF_Base ||
		  qubimptype->m_refkind == REF_NoName,
                  "IMPMGR::DebChkState: invalid RefKind");

    }

    // In Ole there is nothing in the free list. All entries are valid
    // entries

    //
    DebAssert(cbSize / sizeof(IMPTYPE) == cEntries,
	      "IMPMGR::DebCheckState: Free list messed up");

}
#pragma code_seg()




/***
*PUBLIC IMPMGR::DebChkHimptype
*Purpose:
*   Check whether an himptype is valid
*
*Entry:
*   himptype
*
*Exit:
*   None.
*
***********************************************************************/

void IMPMGR::DebChkHimptype(HIMPTYPE himptype) const
{

DebAssert( (himptype < m_pbdTimptype->CbSize() &&
	  (himptype % sizeof(IMPTYPE)) == 0 ) ||
	  ( m_fCheckRemainingDepCalled == FALSE ),
         "DebChkHimptype: invalid import entry");
}


/***
*PUBLIC IMPMGR::DebChkHimpaddr
*Purpose:
*   Check whether an himpaddr is valid
*
*Entry:
*   himpaddr
*
*Exit:
*   None.
*
***********************************************************************/

void IMPMGR::DebChkHimpaddr(HIMPADDR himpaddr) const
{
}
#pragma code_seg()

#endif 



// catches compiler generated code for static construction.
#pragma code_seg(CS_STATIC_INIT)
