mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1164 lines
38 KiB
1164 lines
38 KiB
/***
|
|
*frame.cxx - The frame handler and everything associated with it.
|
|
*
|
|
* Copyright (c) 1993-1995, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* The frame handler and everything associated with it.
|
|
*
|
|
* Entry points:
|
|
* _CxxFrameHandler - the frame handler.
|
|
*
|
|
* Open issues:
|
|
* * Handling re-throw from dynamicly nested scope.
|
|
* * Fault-tolerance (checking for data structure validity).
|
|
*
|
|
*Revision History:
|
|
* 05-20-93 BS Module created
|
|
* 03-22-95 PML Add const for read-only compiler-gen'd structs
|
|
****/
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _WIN32
|
|
extern "C" {
|
|
#include <windows.h>
|
|
};
|
|
|
|
#include <mtdll.h> // CRT internal header file
|
|
#endif
|
|
|
|
#include "ehassert.h" // This project's versions of standard assert macros
|
|
#include "ehdata.h" // Declarations of all types used for EH
|
|
#ifdef _WIN32
|
|
#include "trnsctrl.h" // Routines to handle transfer of control (trnsctrl.asm)
|
|
#else
|
|
extern "C" {
|
|
#include "trnsctrl.h"
|
|
}
|
|
#endif
|
|
#include "ehstate.h" // Declarations of state management stuff
|
|
#include "eh.h" // User-visible routines for eh
|
|
#include "ehhooks.h" // Declarations of hook variables and callbacks
|
|
|
|
#pragma hdrstop // Pch is created from here
|
|
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
extern "C" {
|
|
extern __declspec(allocate("_CODE")) struct {
|
|
unsigned long dwSig;
|
|
unsigned long uoffDestination;
|
|
unsigned long dwCode;
|
|
} _NLG_Destination;
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Forward declaration of local functions:
|
|
//
|
|
|
|
// M00TODO: all these parameters should be declared const
|
|
|
|
static void FindHandler ( EHExceptionRecord*, EHRegistrationNode*, void*, DispatcherContext*, FuncInfo*, BOOLEAN, int, EHRegistrationNode* );
|
|
static void FindHandlerForForeignException( EHExceptionRecord*, EHRegistrationNode*, void*, DispatcherContext*, FuncInfo*, __ehstate_t, int, EHRegistrationNode* );
|
|
static void FrameUnwindToState ( EHRegistrationNode*, DispatcherContext*, FuncInfo*, __ehstate_t );
|
|
#ifdef _WIN32
|
|
static TryBlockMapEntry*
|
|
GetRangeOfTrysToCheck( FuncInfo*, int, __ehstate_t, unsigned*, unsigned*);
|
|
#else
|
|
static TryBlockMapEntry*
|
|
GetRangeOfTrysToCheck( EHRegistrationNode*, FuncInfo*, int, __ehstate_t, unsigned*, unsigned*);
|
|
#endif
|
|
static inline int TypeMatch ( HandlerType*, CatchableType*, ThrowInfo* );
|
|
static void CatchIt ( EHExceptionRecord*, EHRegistrationNode*, void*, DispatcherContext*, FuncInfo*, HandlerType*, CatchableType*, TryBlockMapEntry*, int, EHRegistrationNode* );
|
|
#ifdef _WIN32
|
|
static void * CallCatchBlock ( EHExceptionRecord*, EHRegistrationNode*, void*, FuncInfo*, void*, int );
|
|
#else
|
|
extern "C" {
|
|
extern void * CallCatchBlock ( EHExceptionRecord*, EHRegistrationNode*, void*, FuncInfo*, void*, int );
|
|
extern int GetCatchDepth(EHRegistrationNode*);
|
|
extern void * GetCurThrowFrame(void);
|
|
}
|
|
#endif
|
|
static void BuildCatchObject ( EHExceptionRecord*, EHRegistrationNode*, HandlerType*, CatchableType* );
|
|
#ifdef _WIN32
|
|
static int ExFilterRethrow ( EXCEPTION_POINTERS * );
|
|
#endif
|
|
#ifdef _WIN32
|
|
static void DestructExceptionObject( EHExceptionRecord*, BOOLEAN );
|
|
#else
|
|
extern "C" {
|
|
void DestructExceptionObject( EHExceptionRecord*, BOOLEAN );
|
|
}
|
|
#endif
|
|
|
|
static void * AdjustPointer ( void*, const PMD& );
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// Make sure the terminate wrapper is dragged in:
|
|
//
|
|
static void*pMyUnhandledExceptionFilter = &__CxxUnhandledExceptionFilter;
|
|
#endif
|
|
|
|
//
|
|
// This describes the most recently handled exception, in case of a rethrow:
|
|
//
|
|
#ifdef _MT
|
|
#define pCurrentException (*((EHExceptionRecord**) &(_getptd()->_curexception)))
|
|
#define pCurrentExContext (*((void **) &(_getptd()->_curcontext)))
|
|
#else
|
|
static EHExceptionRecord *pCurrentException = NULL;
|
|
static void *pCurrentExContext = NULL;
|
|
#endif
|
|
|
|
|
|
//handles throw within a destructor
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
int __fInsideDestructor = FALSE;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// __InternalCxxFrameHandler - the frame handler for all functions with C++ EH
|
|
// information.
|
|
//
|
|
// If exception is handled, this doesn't return.
|
|
// Otherwise, it returns ExceptionContinueSearch.
|
|
//
|
|
// Note that this is called three ways:
|
|
// From __CxxFrameHandler: primary usage, called to inspect whole function.
|
|
// CatchDepth==0, pMarkerRN==NULL
|
|
// From CatchGuardHandler: If an exception occurred within a catch, this is
|
|
// called to check for try blocks within that catch only, and does not
|
|
// handle unwinds.
|
|
// From TranslatorGuardHandler: Called to handle the translation of a non-C++
|
|
// EH exception. Context considered is that of parent.
|
|
//
|
|
extern "C" EXCEPTION_DISPOSITION __cdecl __InternalCxxFrameHandler(
|
|
EHExceptionRecord *pExcept, // Information for this exception
|
|
EHRegistrationNode *pRN, // Dynamic information for this frame
|
|
void *pContext, // Context info (we don't care what's in it)
|
|
DispatcherContext *pDC, // Context within subject frame
|
|
FuncInfo *pFuncInfo, // Static information for this frame
|
|
int CatchDepth, // How deeply nested are we?
|
|
EHRegistrationNode *pMarkerRN, // Marker node for when checking inside
|
|
// catch block
|
|
BOOL recursive // Are we handling a translation?
|
|
) {
|
|
DASSERT(FUNC_MAGICNUM(*pFuncInfo) == EH_MAGIC_NUMBER1);
|
|
|
|
if (IS_UNWINDING(PER_FLAGS(pExcept)))
|
|
{
|
|
//
|
|
// We're at the unwinding stage of things. Don't care about the
|
|
// exception itself. (Check this first because it's easier)
|
|
//
|
|
|
|
if (FUNC_MAXSTATE(*pFuncInfo) != 0 && CatchDepth == 0)
|
|
{
|
|
//
|
|
// Only unwind if there's something to unwind
|
|
// AND we're being called through the primary RN.
|
|
//
|
|
FrameUnwindToState(pRN, pDC, pFuncInfo, EH_EMPTY_STATE);
|
|
}
|
|
|
|
return ExceptionContinueSearch; // I don't think this value matters
|
|
}
|
|
else if (FUNC_NTRYBLOCKS(*pFuncInfo) != 0)
|
|
{
|
|
//
|
|
// NT is looking for handlers. We've got handlers.
|
|
// Let's check this puppy out. Do we recognize it?
|
|
//
|
|
|
|
if ( (PER_CODE(pExcept) == EH_EXCEPTION_NUMBER)
|
|
&& (PER_MAGICNUM(pExcept) > EH_MAGIC_NUMBER1)
|
|
&& (THROW_FORWARDCOMPAT(*PER_PTHROW(pExcept)) != NULL) )
|
|
{
|
|
//
|
|
// Forward compatibility: The thrown object appears to have been
|
|
// created by a newer version of our compiler. Let that version's
|
|
// frame handler do the work (if one was specified).
|
|
//
|
|
|
|
#ifdef _WIN32
|
|
if ( ValidateExecute( (FARPROC)THROW_FORWARDCOMPAT(
|
|
*PER_PTHROW(pExcept)) ) )
|
|
#endif
|
|
{
|
|
return (EXCEPTION_DISPOSITION)
|
|
THROW_FORWARDCOMPAT( *PER_PTHROW(pExcept))
|
|
( pExcept, pRN, pContext, pDC, pFuncInfo, CatchDepth, pMarkerRN, recursive );
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
_inconsistency(); // Does not return; TKB
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Anything else: we'll handle it here.
|
|
//
|
|
FindHandler( pExcept, pRN, pContext, pDC, pFuncInfo, recursive, CatchDepth, pMarkerRN );
|
|
|
|
}
|
|
|
|
// If it returned, we didn't have any matches.
|
|
|
|
} /* NT was looking for a handler */
|
|
|
|
//
|
|
// We had nothing to do with it or it was rethrown. Keep searching.
|
|
//
|
|
|
|
return ExceptionContinueSearch;
|
|
|
|
} /* InternalCxxFrameHandler */
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FindHandler - find a matching handler on this frame, using all means
|
|
// available.
|
|
//
|
|
// Description:
|
|
// * If the exception thrown was an MSC++ EH, search handlers for match.
|
|
// * Otherwise, if we haven't already recursed, try to translate.
|
|
// * If we have recursed (ie we're handling the translator's exception),
|
|
// and it isn't a typed exception, call _inconsistency.
|
|
//
|
|
// Returns:
|
|
// Returns iff exception was not handled.
|
|
//
|
|
// Assumptions:
|
|
// * Only called if there are handlers in this function.
|
|
//
|
|
|
|
static void FindHandler(
|
|
EHExceptionRecord *pExcept, // Information for this (logical) exception
|
|
EHRegistrationNode *pRN, // Dynamic information for subject frame
|
|
void *pContext, // Context info (we don't care what's in it)
|
|
DispatcherContext *pDC, // Context within subject frame
|
|
FuncInfo *pFuncInfo, // Static information for subject frame
|
|
BOOLEAN recursive, // TRUE if we're handling the translation
|
|
int CatchDepth, // Level of nested catch that is being checked
|
|
EHRegistrationNode *pMarkerRN // Extra marker RN for nested catch handling
|
|
)
|
|
{
|
|
|
|
//
|
|
// Get the current state (mach. dep.)
|
|
//
|
|
__ehstate_t curState = GetCurrentState(pRN, pDC, pFuncInfo);
|
|
DASSERT((curState >= EH_EMPTY_STATE) && (curState < FUNC_MAXSTATE(*pFuncInfo)));
|
|
|
|
//
|
|
// Check if it's a re-throw. Use the exception we stashed away if it is.
|
|
//
|
|
if ( PER_IS_MSVC_EH(pExcept)
|
|
&&
|
|
PER_PTHROW(pExcept) == NULL
|
|
) {
|
|
if ( pCurrentException == NULL ) {
|
|
//
|
|
// Oops! User re-threw a non-existant exception! Let it propogate.
|
|
//
|
|
return;
|
|
}
|
|
|
|
pExcept = pCurrentException;
|
|
pContext = pCurrentExContext;
|
|
#ifdef _WIN32
|
|
DASSERT( ValidateRead(pExcept) );
|
|
#endif
|
|
DASSERT( !PER_IS_MSVC_EH(pExcept) || PER_PTHROW(pExcept) != NULL );
|
|
}
|
|
|
|
|
|
if ( PER_IS_MSVC_EH(pExcept) )
|
|
{
|
|
//
|
|
// Looks like it's ours. Let's see if we have a match:
|
|
//
|
|
// First, determine range of try blocks to consider:
|
|
// Only try blocks which are at the current catch depth are of interest.
|
|
//
|
|
unsigned curTry;
|
|
unsigned end;
|
|
TryBlockMapEntry *pEntry = GetRangeOfTrysToCheck(pRN, pFuncInfo, CatchDepth, curState, &curTry, &end);
|
|
|
|
//
|
|
// Scan the try blocks in the function:
|
|
//
|
|
// Change to curTry <= end, (it was curTry < end)
|
|
for( ; curTry <= end; curTry++, pEntry++ )
|
|
{
|
|
if (TBME_LOW(*pEntry) <= curState && curState <= TBME_HIGH(*pEntry))
|
|
{
|
|
//
|
|
// Try block was in scope for current state.
|
|
// Scan catches for this try:
|
|
//
|
|
HandlerType *pCatch = TBME_PCATCH(*pEntry, 0);
|
|
for( int catches = TBME_NCATCHES(*pEntry);
|
|
catches; catches--, pCatch++ )
|
|
{
|
|
//
|
|
// Scan all types that thrown object can be converted to:
|
|
//
|
|
|
|
CatchableType * const *ppCatchable = THROW_CTLIST(*PER_PTHROW(pExcept));
|
|
for( int catchables = THROW_COUNT(*PER_PTHROW(pExcept));
|
|
catchables; catchables--, ppCatchable++ )
|
|
{
|
|
if (TypeMatch( pCatch, *ppCatchable, PER_PTHROW(pExcept)) )
|
|
{
|
|
//
|
|
// OK. We finally found a match. Activate the catch.
|
|
// If control gets back here, the catch did a re-throw,
|
|
// so keep searching.
|
|
//
|
|
|
|
CatchIt( pExcept, pRN, pContext, pDC, pFuncInfo,
|
|
pCatch, *ppCatchable, pEntry,
|
|
CatchDepth, pMarkerRN );
|
|
goto NextTryBlock;
|
|
|
|
}
|
|
} /* Scan posible conversions */
|
|
} /* Scan catch clauses */
|
|
} /* Try was in scope */
|
|
NextTryBlock: ;
|
|
} /* Scan try blocks */
|
|
|
|
if (recursive)
|
|
{
|
|
//
|
|
// A translation was provided, but this frame didn't catch it.
|
|
// Destruct the translated object before returning;
|
|
// if destruction raises an exception, issue _inconsistency.
|
|
//
|
|
|
|
DestructExceptionObject( pExcept, TRUE );
|
|
}
|
|
|
|
} /* It was a C++ EH exception */
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
//
|
|
// Not ours. But maybe someone told us how to make it ours.
|
|
//
|
|
|
|
if ( !recursive )
|
|
{
|
|
FindHandlerForForeignException( pExcept, pRN, pContext, pDC, pFuncInfo, curState, CatchDepth, pMarkerRN );
|
|
} /* not recursive */
|
|
else
|
|
{
|
|
//
|
|
// We're recursive, and the exception wasn't a C++ EH!
|
|
// Translator threw something uninteligable. We're outa here!
|
|
//
|
|
|
|
// M00REVIEW: Two choices here actually: we could let the new
|
|
// exception take over.
|
|
|
|
terminate();
|
|
}
|
|
} /* it wasn't our exception */
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FindHandlerForForeignException - We've got an exception which wasn't ours.
|
|
// Try to translate it into C++ EH, and also check for match with
|
|
// ellipsis.
|
|
//
|
|
// Description:
|
|
// If an SE-to-EH translator has been installed, call it. The
|
|
// translator must throw the appropriate typed exception or return. If
|
|
// the translator throws, we invoke FindHandler again as the exception
|
|
// filter.
|
|
//
|
|
// Returns:
|
|
// Returns if exception was not fully handled.
|
|
// No return value.
|
|
//
|
|
// Assumptions:
|
|
// Only called if there are handlers in this function.
|
|
//
|
|
static void FindHandlerForForeignException(
|
|
EHExceptionRecord *pExcept, // Information for this (logical) exception
|
|
EHRegistrationNode *pRN, // Dynamic information for subject frame
|
|
void *pContext, // Context info (we don't care what's in it)
|
|
DispatcherContext *pDC, // Context within subject frame
|
|
FuncInfo *pFuncInfo, // Static information for subject frame
|
|
__ehstate_t curState, // Current state
|
|
int CatchDepth, // Level of nested catch that is being checked
|
|
EHRegistrationNode *pMarkerRN // Extra marker RN for nested catch handling
|
|
)
|
|
{
|
|
if (__pSETranslator != NULL)
|
|
{
|
|
//
|
|
// Call the translator. If the translator knows what to
|
|
// make of it, it will throw an appropriate C++ exception.
|
|
// We intercept it and use it (recursively) for this
|
|
// frame. Don't recurse more than once.
|
|
//
|
|
if (CallSETranslator( pExcept, pRN, pContext, pDC, pFuncInfo, CatchDepth, pMarkerRN )) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Didn't have a translator, or the translator returned normally
|
|
// (ie didn't translate it). Still need to check for match
|
|
// with ellipsis:
|
|
//
|
|
|
|
unsigned curTry;
|
|
unsigned end;
|
|
TryBlockMapEntry *pEntry = GetRangeOfTrysToCheck(pFuncInfo, CatchDepth, curState, &curTry, &end);
|
|
|
|
//
|
|
// Scan the try blocks in the function:
|
|
//
|
|
for( ; curTry < end; curTry++, pEntry++ )
|
|
{
|
|
//
|
|
// If the try-block was in scope
|
|
// AND
|
|
// The last catch in that try is an ellipsis (no other can be)
|
|
//
|
|
if ( (curState >= TBME_LOW(*pEntry) && curState <= TBME_HIGH(*pEntry))
|
|
&& HT_IS_TYPE_ELLIPSIS( TBME_CATCH(*pEntry,
|
|
TBME_NCATCHES(*pEntry) - 1) ) )
|
|
{
|
|
//
|
|
// Found an ellipsis. Handle exception.
|
|
//
|
|
CatchIt( pExcept, pRN, pContext, pDC, pFuncInfo,
|
|
TBME_PCATCH(*pEntry, TBME_NCATCHES(*pEntry) - 1),
|
|
NULL, pEntry, CatchDepth, pMarkerRN );
|
|
|
|
// If it returns, handler re-threw. Keep searching.
|
|
|
|
} /* have ellipsis in scope */
|
|
} /* search for try */
|
|
|
|
//
|
|
// If we got here, that means we didn't have anything to do with the
|
|
// exception. Continue search.
|
|
//
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetRangeOfTrysToCheck - determine which try blocks are of interest, given
|
|
// the current catch block nesting depth. We only check the trys at a single
|
|
// depth.
|
|
//
|
|
// Returns:
|
|
// Address of first try block of interest is returned
|
|
// pStart and pEnd get the indices of the range in question
|
|
//
|
|
static TryBlockMapEntry* GetRangeOfTrysToCheck(
|
|
EHRegistrationNode* pRN,
|
|
FuncInfo *pFuncInfo,
|
|
int CatchDepth,
|
|
__ehstate_t curState,
|
|
unsigned *pStart,
|
|
unsigned *pEnd
|
|
) {
|
|
TryBlockMapEntry *pEntry = FUNC_PTRYBLOCK(*pFuncInfo, 0);
|
|
unsigned start = FUNC_NTRYBLOCKS(*pFuncInfo);
|
|
unsigned end = start;
|
|
unsigned end1 = end;
|
|
unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo);
|
|
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
CatchDepth = GetCatchDepth(pRN);
|
|
#endif
|
|
|
|
while (CatchDepth >= 0) {
|
|
DASSERT(start != -1);
|
|
start--;
|
|
//change to <= curState (it was < curState)
|
|
if ( (start != -1) && (TBME_LOW(pEntry[start]) <= curState) && (curState <= TBME_HIGH(pEntry[start])) && (curState <= TBME_CATCHHIGH(pEntry[start])) )
|
|
// || (start == -1)
|
|
{
|
|
if (start != -1)
|
|
{
|
|
if (start != 0 && CatchDepth == 0)
|
|
{
|
|
; //we do want to find inner most catch
|
|
}
|
|
else
|
|
{
|
|
CatchDepth--;
|
|
}
|
|
end = end1;
|
|
end1 = start;
|
|
}
|
|
else //we are over the first one, set it to the first one
|
|
{
|
|
CatchDepth= -1;
|
|
start = 0;
|
|
end = 0;
|
|
}
|
|
}
|
|
else if (start == -1)
|
|
{
|
|
CatchDepth--;
|
|
start = end1;
|
|
}
|
|
}
|
|
|
|
// MAC version: made change above, so there is no overshoot
|
|
// *pStart = ++start; // We always overshoot by 1 (we may even wrap around)
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
if (end == FUNC_NTRYBLOCKS(*pFuncInfo))
|
|
{
|
|
end--;
|
|
if (start > end)
|
|
{
|
|
start--;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*pStart = start;
|
|
*pEnd = end;
|
|
|
|
DASSERT( end <= FUNC_NTRYBLOCKS(*pFuncInfo) && start <= end );
|
|
return &(pEntry[start]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// TypeMatch - check if the catch type matches the given throw conversion.
|
|
//
|
|
// Returns TRUE if the catch can catch using this throw conversion, FLASE
|
|
// otherwise.
|
|
//
|
|
|
|
static inline int TypeMatch (
|
|
HandlerType *pCatch, // Type of the 'catch' clause
|
|
CatchableType *pCatchable, // Type conversion under consideration
|
|
ThrowInfo *pThrow // General information about the
|
|
// thrown type.
|
|
) {
|
|
//
|
|
// First, check for match with ellipsis:
|
|
//
|
|
|
|
if (HT_IS_TYPE_ELLIPSIS(*pCatch)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Not ellipsis; check if the basic types match:
|
|
//
|
|
|
|
if ( (HT_HASH(*pCatch) == CT_HASH(*pCatchable)) // Hash values match
|
|
&& // AND
|
|
( (HT_PTD(*pCatch) == CT_PTD(*pCatchable)) // It's the same record
|
|
|| // OR the names compare the same
|
|
(strcmp(HT_NAME(*pCatch), CT_NAME(*pCatchable)) == 0))
|
|
) {
|
|
|
|
//
|
|
// Basic types match. Now check if the actual conversion is valid:
|
|
//
|
|
|
|
if ( (!CT_BYREFONLY(*pCatchable) || HT_ISREFERENCE(*pCatch)) // Caught by ref if ref required
|
|
&& // AND
|
|
(!THROW_ISCONST(*pThrow) || HT_ISCONST(*pCatch)) // The qualifiers are compatible
|
|
&&
|
|
(!THROW_ISVOLATILE(*pThrow) || HT_ISVOLATILE(*pCatch))
|
|
#if _M_MRX000
|
|
&&
|
|
(!THROW_ISUNALIGNED(*pThrow) || HT_ISUNALIGNED(*pCatch))
|
|
#endif
|
|
) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If any test failed, we don't have a match.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FrameUnwindToState - unwind this frame until the specified state is reached.
|
|
//
|
|
// No return value.
|
|
//
|
|
// Side Effects:
|
|
// * All objects on frame which go out of scope as a result of the
|
|
// unwind are destructed.
|
|
// * Registration node is updated to reflect new state.
|
|
//
|
|
// Usage:
|
|
// This function is called both to do full-frame unwind during the unwind
|
|
// phase (targetState = -1), and to do partial unwinding when the current
|
|
// frame has an appropriate catch.
|
|
//
|
|
|
|
static void FrameUnwindToState (
|
|
EHRegistrationNode *pRN, // Registration node for subject function
|
|
DispatcherContext *pDC, // Context within subject frame
|
|
FuncInfo *pFuncInfo, // Static information for subject function
|
|
__ehstate_t targetState // State to unwind to
|
|
) {
|
|
__ehstate_t curState = GetCurrentState( pRN, pDC, pFuncInfo );
|
|
|
|
#ifdef _WIN32
|
|
__try {
|
|
#endif
|
|
while (curState != targetState) {
|
|
DASSERT((curState > EH_EMPTY_STATE) && (curState < FUNC_MAXSTATE(*pFuncInfo)));
|
|
|
|
//
|
|
// Call the unwind action (if one exists):
|
|
//
|
|
if (UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)) != NULL) {
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
__fInsideDestructor = TRUE;
|
|
#endif
|
|
//for debugger support, enter destructor
|
|
_NLG_Destination.dwCode = 0x103;
|
|
CallSettingFrame( UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)), pRN );
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
__fInsideDestructor = FALSE;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Adjust the state:
|
|
//
|
|
|
|
curState = UWE_TOSTATE(FUNC_UNWIND(*pFuncInfo, curState));
|
|
}
|
|
#ifdef _WIN32
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Something went wrong while unwinding. Don't care what it was.
|
|
//
|
|
terminate();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Now that we're done, set the frame to reflect the final state.
|
|
//
|
|
|
|
DASSERT( curState == targetState );
|
|
|
|
SetState( pRN, pDC, pFuncInfo, curState );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CatchIt - A handler has been found for the thrown type. Do the work to
|
|
// transfer control.
|
|
//
|
|
// Description:
|
|
// * Builds the catch object
|
|
// * Unwinds the stack to the point of the try
|
|
// * Calls the address of the handler (funclet) with the frame set up
|
|
// for that function but without resetting the stack.
|
|
// * Handler funclet returns address to continue execution, or NULL if
|
|
// the handler re-threw ("throw;" lexically in handler)
|
|
// * If the handler throws an EH exception whose exception info is NULL,
|
|
// then it's a re-throw from a dynamicly enclosed scope.
|
|
//
|
|
// M00REVIEW: It is still an open question whether the catch object is built
|
|
// before or after the local unwind.
|
|
//
|
|
// Returns:
|
|
// No return value. Returns iff handler re-throws.
|
|
//
|
|
|
|
static void CatchIt (
|
|
EHExceptionRecord *pExcept, // The exception thrown
|
|
EHRegistrationNode *pRN, // Dynamic info of function with catch
|
|
void *pContext, // Context info (we don't care what's in it)
|
|
DispatcherContext *pDC, // Context within subject frame
|
|
FuncInfo *pFuncInfo, // Static info of function with catch
|
|
HandlerType *pCatch, // The catch clause selected
|
|
CatchableType *pConv, // The rules for making the conversion
|
|
TryBlockMapEntry *pEntry, // Description of the try block
|
|
int CatchDepth, // How many catches are we nested in?
|
|
EHRegistrationNode *pMarkerRN // Special node if nested in catch
|
|
) {
|
|
void *continuationAddress;
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
|
|
EHExceptionRecord *pSaveException = pCurrentException;
|
|
void *pSaveExContext = pCurrentExContext;
|
|
pCurrentException = pExcept;
|
|
pCurrentExContext = pContext;
|
|
#endif
|
|
|
|
//
|
|
// Copy the thrown object into a buffer in the handler's stack frame,
|
|
// unless the catch was by elipsis (no conversion) OR the catch was by
|
|
// type without an actual 'catch object'.
|
|
//
|
|
if ( pConv != NULL )
|
|
{
|
|
BuildCatchObject( pExcept, pRN, pCatch, pConv );
|
|
}
|
|
|
|
//
|
|
// Unwind stack objects to the entry of the try that caught this exception.
|
|
//
|
|
if (pMarkerRN == NULL) {
|
|
UnwindNestedFrames( pRN, pExcept ); // Mach. dependent
|
|
}
|
|
else {
|
|
UnwindNestedFrames( pMarkerRN, pExcept );
|
|
}
|
|
FrameUnwindToState( pRN, pDC, pFuncInfo, TBME_LOW(*pEntry) );
|
|
|
|
//
|
|
// Call the catch. Separated out because it introduces a new registration
|
|
// node.
|
|
//
|
|
SetState( pRN, pDC, pFuncInfo, TBME_HIGH(*pEntry) + 1 );
|
|
//for debugger support, enter catch handler
|
|
_NLG_Destination.dwCode = 0x100;
|
|
continuationAddress = CallCatchBlock( pExcept, pRN, pContext, pFuncInfo, HT_HANDLER(*pCatch), CatchDepth );
|
|
|
|
#if defined(_M_M68K) || defined(_M_MPPC)
|
|
pCurrentException = pSaveException;
|
|
pCurrentExContext = pSaveExContext;
|
|
#endif
|
|
|
|
//
|
|
// Transfer control to the continuation address. If no continuation then
|
|
// it's a re-throw, so return.
|
|
//
|
|
|
|
if (continuationAddress != NULL) {
|
|
JumpToContinuation( continuationAddress, pRN );
|
|
// No return.
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CallCatchBlock - continuation of CatchIt.
|
|
//
|
|
// This is seperated from CatchIt because it needs to introduce an SEH frame
|
|
// in case the catch block throws. This frame cannot be added until unwind of
|
|
// nested frames has been completed (otherwise this frame would be the first
|
|
// to go).
|
|
//
|
|
|
|
static void *CallCatchBlock (
|
|
EHExceptionRecord *pExcept, // The exception thrown
|
|
EHRegistrationNode *pRN, // Dynamic info of function with catch
|
|
void *pContext, // Context info (we don't care what's in it)
|
|
FuncInfo *pFuncInfo, // Static info of function with catch
|
|
void *handlerAddress, // Code address of handler
|
|
int CatchDepth // How deeply nested in catch blocks are we?
|
|
) {
|
|
void *continuationAddress = handlerAddress;
|
|
// Address where execution resumes after exception
|
|
// handling completed. Initialized to non-NULL (value
|
|
// doesn't matter) to distinguish from re-throw in finally.
|
|
|
|
//
|
|
// The stack pointer at entry to the try must be saved, in case there is
|
|
// another try inside this catch. We'll restore it on our way out.
|
|
//
|
|
void *saveESP = PRN_STACK(pRN);
|
|
|
|
//
|
|
// Save the current exception in case of a rethrow. Save the previous value
|
|
// on the stack, to be restored when the catch exits.
|
|
//
|
|
EHExceptionRecord *pSaveException = pCurrentException;
|
|
void *pSaveExContext = pCurrentExContext;
|
|
pCurrentException = pExcept;
|
|
pCurrentExContext = pContext;
|
|
|
|
__try {
|
|
__try {
|
|
//
|
|
// Execute the handler as a funclet, whose return value is
|
|
// the address to resume execution.
|
|
//
|
|
continuationAddress = CallCatchBlock2( pRN, pFuncInfo, handlerAddress, CatchDepth );
|
|
|
|
}
|
|
__except( ExFilterRethrow(exception_info()) ) {
|
|
//
|
|
// If the handler threw a typed exception without exception info
|
|
// or exception object, then it's a re-throw, so return. Otherwise
|
|
// it's a new exception, which takes precedence over this one.
|
|
//
|
|
// Note that the assign isn't redundant; see __finally
|
|
//
|
|
continuationAddress = NULL;
|
|
return NULL;
|
|
|
|
}
|
|
}
|
|
__finally {
|
|
|
|
//
|
|
// Restore the saved stack pointer, so the stack can be reset when
|
|
// we're done.
|
|
//
|
|
PRN_STACK(pRN) = saveESP;
|
|
|
|
//
|
|
// Restore the 'current exception' for a possibly enclosing catch
|
|
//
|
|
pCurrentException = pSaveException;
|
|
pCurrentExContext = pSaveExContext;
|
|
|
|
//
|
|
// Destroy the original exception object if we're not exiting on a
|
|
// re-throw. (note that the catch handles destruction of its parameter).
|
|
//
|
|
if ( PER_IS_MSVC_EH(pExcept)
|
|
&& (continuationAddress != NULL) )
|
|
{
|
|
DestructExceptionObject( pExcept, abnormal_termination() );
|
|
}
|
|
}
|
|
|
|
return continuationAddress;
|
|
|
|
} /* CallCatchBlock */
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ExFilterRethrow - exception filter for re-throw exceptions.
|
|
//
|
|
// Returns:
|
|
// EXCEPTION_EXECUTE_HANDLER - exception was a re-throw
|
|
// EXCEPTION_CONTINUE_SEARCH - anything else
|
|
//
|
|
// Side-effects: NONE.
|
|
//
|
|
|
|
static int ExFilterRethrow (
|
|
EXCEPTION_POINTERS *pExPtrs
|
|
) {
|
|
//
|
|
// Get the exception record thrown (don't care about other info)
|
|
//
|
|
EHExceptionRecord *pExcept = (EHExceptionRecord *)pExPtrs->ExceptionRecord;
|
|
|
|
//
|
|
// Check if it's ours and it's got no exception information.
|
|
//
|
|
if ( PER_IS_MSVC_EH(pExcept)
|
|
&&
|
|
(PER_PTHROW(pExcept) == NULL)
|
|
) {
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
else {
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// BuildCatchObject - copy or construct the catch object from the object
|
|
// thrown.
|
|
//
|
|
// Returns:
|
|
// nothing.
|
|
//
|
|
// Side-effects:
|
|
// A buffer in the subject function's frame is initialized.
|
|
//
|
|
// Open issues:
|
|
// * What happens if the constructor throws? (or faults?)
|
|
//
|
|
|
|
static void BuildCatchObject (
|
|
EHExceptionRecord *pExcept, // Original exception thrown
|
|
EHRegistrationNode *pRN, // Registration node of catching function
|
|
HandlerType *pCatch, // The catch clause that got it
|
|
CatchableType *pConv // The conversion to use
|
|
) {
|
|
if (HT_IS_TYPE_ELLIPSIS(*pCatch) || !HT_DISPCATCH(*pCatch) ) {
|
|
//
|
|
// If the catch is by ellipsis, then there is no object to construct.
|
|
// If the catch is by type(No Catch Object), then leave too!
|
|
//
|
|
return;
|
|
}
|
|
|
|
void **pCatchBuffer = (void**)OffsetToAddress(HT_DISPCATCH(*pCatch), pRN);
|
|
|
|
#ifdef _WIN32
|
|
__try {
|
|
#endif
|
|
if (HT_ISREFERENCE(*pCatch)) {
|
|
//
|
|
// The catch is of form 'reference to T'. At the throw point we treat
|
|
// both 'T' and 'reference to T' the same, ie pExceptionObject is a
|
|
// (machine) pointer to T.
|
|
// adjust as required.
|
|
//
|
|
#ifdef _WIN32
|
|
if ( ValidateRead( PER_PEXCEPTOBJ(pExcept) ) &&
|
|
ValidateWrite( pCatchBuffer )
|
|
)
|
|
#endif
|
|
{
|
|
*pCatchBuffer = PER_PEXCEPTOBJ(pExcept);
|
|
*pCatchBuffer = AdjustPointer( *pCatchBuffer,
|
|
CT_THISDISP(*pConv)
|
|
);
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
_inconsistency(); // Does not return; TKB
|
|
}
|
|
#endif
|
|
}
|
|
else if (CT_ISSIMPLETYPE(*pConv)) {
|
|
//
|
|
// Object thrown is of simple type (this including pointers)
|
|
// copy specified number of bytes.
|
|
// adjust the pointer as required.
|
|
// if the thing is not a pointer, then this should be safe
|
|
// since all the entries in the THISDISP are 0.
|
|
//
|
|
#ifdef _WIN32
|
|
if( ValidateRead( PER_PEXCEPTOBJ(pExcept) )
|
|
&&
|
|
ValidateWrite( pCatchBuffer )
|
|
)
|
|
#endif
|
|
{
|
|
memmove( pCatchBuffer,
|
|
PER_PEXCEPTOBJ(pExcept),
|
|
CT_SIZE(*pConv)
|
|
);
|
|
if (*pCatchBuffer != NULL) {
|
|
*pCatchBuffer = AdjustPointer( *pCatchBuffer,
|
|
CT_THISDISP(*pConv)
|
|
);
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
_inconsistency(); // Does not return; TKB
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
//
|
|
// Object thrown is UDT.
|
|
//
|
|
if (CT_COPYFUNC(*pConv) == NULL) {
|
|
//
|
|
// the UDT had a simple ctor.
|
|
// adjust in the thrown object, then copy n bytes.
|
|
//
|
|
|
|
#ifdef _WIN32
|
|
if (ValidateRead( PER_PEXCEPTOBJ(pExcept) )
|
|
&&
|
|
ValidateWrite( pCatchBuffer )
|
|
)
|
|
#endif
|
|
{
|
|
memmove(pCatchBuffer,
|
|
AdjustPointer( PER_PEXCEPTOBJ(pExcept),
|
|
CT_THISDISP(*pConv) ),
|
|
CT_SIZE(*pConv)
|
|
);
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
_inconsistency(); // Does not return; TKB
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
//
|
|
// It's a UDT: make a copy using copy ctor
|
|
//
|
|
#ifdef _WIN32
|
|
if( ValidateRead( PER_PEXCEPTOBJ(pExcept) )
|
|
&&
|
|
ValidateWrite( pCatchBuffer )
|
|
&&
|
|
ValidateExecute( (FARPROC)CT_COPYFUNC( *pConv ) ) )
|
|
#endif
|
|
{
|
|
if (CT_HASVB(*pConv)) {
|
|
CallMemberFunction2(
|
|
(char*)pCatchBuffer,
|
|
CT_COPYFUNC(*pConv),
|
|
AdjustPointer( PER_PEXCEPTOBJ(pExcept),
|
|
CT_THISDISP(*pConv)),
|
|
1);
|
|
}
|
|
else {
|
|
CallMemberFunction1(
|
|
(char*)pCatchBuffer,
|
|
CT_COPYFUNC(*pConv),
|
|
AdjustPointer( PER_PEXCEPTOBJ(pExcept),
|
|
CT_THISDISP(*pConv)) );
|
|
}
|
|
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
_inconsistency(); // Does not return; TKB
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Something went wrong when building the catch object.
|
|
//
|
|
terminate();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DestructExceptionObject - call the destructor (if any) of the original
|
|
// exception object.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Side-effects(Prime Effect?):
|
|
// Original exception object is destructed.
|
|
//
|
|
// Notes:
|
|
// If destruction throws any exception, and we are destructing the
|
|
// exception object as a result of a new exception, we give up.
|
|
// If the destruction throws otherwise, we let it be.
|
|
//
|
|
#ifdef _WIN32
|
|
static void DestructExceptionObject(
|
|
EHExceptionRecord *pExcept, // The original exception record
|
|
BOOLEAN fThrowNotAllowed // TRUE if destructor not allowed to throw
|
|
) {
|
|
#else
|
|
// public the header for Mac case
|
|
void DestructExceptionObject(
|
|
EHExceptionRecord *pExcept, // The original exception record
|
|
BOOLEAN fThrowNotAllowed // TRUE if destructor not allowed to throw
|
|
) {
|
|
#endif
|
|
|
|
if ( THROW_UNWINDFUNC(*PER_PTHROW(pExcept)) != NULL ) {
|
|
#ifdef _WIN32
|
|
__try {
|
|
#else
|
|
__fInsideDestructor = TRUE;
|
|
#endif
|
|
|
|
// M00REVIEW: A destructor has additional hidden arguments, doesn't it?
|
|
CallMemberFunction0( PER_PEXCEPTOBJ(pExcept),
|
|
THROW_UNWINDFUNC(*PER_PTHROW(pExcept)));
|
|
#ifdef _WIN32
|
|
}
|
|
__except( fThrowNotAllowed ?
|
|
EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH
|
|
) {
|
|
//
|
|
// Can't have new exceptions when we're unwinding due to another
|
|
// exception.
|
|
//
|
|
terminate();
|
|
}
|
|
#else
|
|
__fInsideDestructor = FALSE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AdjustPointer - Adjust the pointer to the exception object to a pointer
|
|
// to a base instance.
|
|
//
|
|
// Output:
|
|
// The address point of the base.
|
|
//
|
|
// Side-effects:
|
|
// NONE.
|
|
//
|
|
|
|
static void *AdjustPointer(
|
|
void* pThis, // Address point of exception object
|
|
const PMD& pmd // Generalized pointer-to-member descriptor
|
|
)
|
|
{
|
|
char *pRet = (char *)pThis + pmd.mdisp;
|
|
|
|
if (pmd.pdisp >= 0)
|
|
#ifdef _WIN32
|
|
pRet += *(ptrdiff_t*)((char*)*((char*)pThis + pmd.pdisp) + pmd.vdisp);
|
|
#else
|
|
pRet += *(ptrdiff_t*)((char*)*(unsigned int *)((char*)pThis + pmd.pdisp) + pmd.vdisp);
|
|
#endif
|
|
return pRet;
|
|
}
|