Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 503

Show
Ignore:
Timestamp:
01/11/11 20:50:01 (14 years ago)
Author:
Don Clugston
Message:

Implements TDPL exception chaining for Windows, using the rule that a chain can be caught if and only if every member of the chain could be caught individually.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/rt/deh.d

    r489 r503  
    155155} 
    156156alias EXCEPTION_RECORD* PEXCEPTION_RECORD, LPEXCEPTION_RECORD; 
    157157 
    158158struct EXCEPTION_POINTERS { 
    159159    PEXCEPTION_RECORD ExceptionRecord; 
    160160    PCONTEXT          ContextRecord; 
    161161} 
    162162alias EXCEPTION_POINTERS* PEXCEPTION_POINTERS, LPEXCEPTION_POINTERS; 
    163163 
    164164enum EXCEPTION_UNWIND = 6;  // Flag to indicate if the system is unwinding 
     165/+ Values used by Microsoft for Itanium and Win64 are: 
     166#define EXCEPTION_NONCONTINUABLE   0x0001 
     167#define EXCEPTION_UNWINDING        0x0002 
     168#define EXCEPTION_EXIT_UNWIND      0x0004 
     169#define EXCEPTION_STACK_INVALID    0x0008 
     170#define EXCEPTION_NESTED_CALL      0x0010 
     171#define EXCEPTION_TARGET_UNWIND    0x0020 
     172#define EXCEPTION_COLLIDED_UNWIND  0x0040 
     173#define EXCEPTION_UNWIND           0x0066 
     174 
     175@@@ BUG @@@ 
     176We don't have any guarantee that this bit will remain available. Unfortunately, 
     177it seems impossible to implement exception handling at all, without relying on 
     178undocumented behaviour in several places. 
     179+/ 
     180enum EXCEPTION_COLLATERAL = 0x100; // Flag used to implement TDPL exception chaining 
    165181 
    166182/* Windows Kernel function to initiate a system unwind. 
    167183 
    168184  Documentation for this function is severely lacking. 
    169185  http://www.nynaeve.net/?p=99 states that the MSDN documentation is incorrect, 
    170186    and gives a corrected form, but it's for x86_64 only. 
    171187  http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was 
    172188    undocumented in 1997. 
    173189  The pExceptRec is what will be passed to the language specific handler. 
    174190  According to MSJ, the targetIp value is unused on Win32. 
     
    297313    DHandlerInfo handler_info[1]; 
    298314}; 
    299315 
    300316struct DCatchBlock 
    301317{ 
    302318    ClassInfo type;            // catch type 
    303319    uint bpoffset;          // EBP offset of catch var 
    304320    void *code;                 // catch handler code 
    305321}; 
    306322 
    307 // Create one of these for each try-catch 
     323// One of these is created for each try-catch 
    308324struct DCatchInfo 
    309325{ 
    310326    uint ncatches;                  // number of catch blocks 
    311327    DCatchBlock catch_block[1];  // data for each catch block 
    312328}; 
    313329 
    314330// Macro to make our own exception code 
    315331template MAKE_EXCEPTION_CODE(int severity, int facility, int exception) 
    316332{ 
    317333        enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception)); 
    318334} 
    319335enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1); 
     336 
     337/* Head of a linked list of all exceptions which are in flight. 
     338 * This is used to implement exception chaining as described in TDPL. 
     339 * Central to making chaining work correctly is that chaining must only occur 
     340 * when a collision occurs (not merely when two exceptions are in flight, 
     341 * because one may be caught before it has any effect on the other). 
     342 * 
     343 * The 'ExceptionRecord' member of the EXCEPTION_RECORD struct is used to 
     344 * store a link to the earlier member on the list. 
     345 * All exceptions which have found their catch handler are linked into this 
     346 * list. The exceptions which collided are marked by setting a bit in the 
     347 * ExceptionFlags. I've called this bit EXCEPTION_COLLATERAL. It has never 
     348 * been used by Microsoft. 
     349 * 
     350 * Every member of the list will either eventually collide with the next earlier 
     351 * exception, having its EXCEPTION_COLLATERAL bit set, or else will be caught. 
     352 * If it is caught, a D exception object is created, containing all of the 
     353 * collateral exceptions, 
     354 * 
     355 * There are many subtleties in this design: 
     356 * (1) The exception records are all on the stack, so it's not possible to 
     357 * modify them very much. In particular, we have very little choice about how 
     358 * unwinding works, so we have to leave all the exception records essentially 
     359 * intact. 
     360 * (2) The length of an exception record is not constant. System exceptions 
     361 * are shorter than D exceptions, for example. 
     362 * (3) System exceptions don't have any space for a pointer to a D object. 
     363 * So we cannot store the collision information in the exception record. 
     364 * (4) it's important that this list is thread-local. 
     365 * 
     366 * Every member of this list will suffer one of two fates: 
     367 * (1) it will be caught successfully. 
     368 *    In this case it is popped from the list. 
     369 *    The previous head of the list, and the previous Throwable chain, are restored. 
     370 * (2) it will collide with the previous exception. 
     371 *    In this case, it gets added to the active Throwable chain, and then discarded. 
     372 * (in which case its Throwable chain will be added to the Throwable chain of 
     373 * the previous exception, and then this exception will be popped from the list). 
     374 * 
     375 * Note: 
     376 */ 
     377 
     378EXCEPTION_RECORD * inflightExceptionList = null; 
     379 
     380/*********************************** 
     381 * Find the first non-collateral exception in the list. If the last 
     382 * entry in the list has the EXCEPTION_COLLATERAL bit set, it means 
     383 * that this fragment will collide with the top exception in the 
     384 * inflightException list. 
     385 */ 
     386EXCEPTION_RECORD *skipCollateralExceptions(EXCEPTION_RECORD *n) 
     387{ 
     388    while ( n.ExceptionRecord && n.ExceptionFlags & EXCEPTION_COLLATERAL ) 
     389    { 
     390        n = n.ExceptionRecord; 
     391    } 
     392    return n; 
     393} 
     394 
    320395 
    321396/*********************************** 
    322397 * The frame handler, this is called for each frame that has been registered 
    323398 * in the OS except_list. 
    324399 * Input: 
    325400 *      EAX     the handler table for the frame 
    326401 */ 
    327402extern(C) 
    328403EXCEPTION_DISPOSITION _d_framehandler( 
    329404            EXCEPTION_RECORD *exceptionRecord, 
     
    360435            phi = &handlerTable.handler_info[ndx]; 
    361436            prev_ndx = phi.prev_index; 
    362437            if (phi.cioffset) 
    363438            { 
    364439                // this is a catch handler (no finally) 
    365440                pci = cast(DCatchInfo *)(cast(ubyte *)handlerTable + phi.cioffset); 
    366441                ncatches = pci.ncatches; 
    367442                for (i = 0; i < ncatches; i++) 
    368443                { 
    369444                    pcb = &pci.catch_block[i]; 
    370  
    371                     if (!ci) 
     445                    // If there are collided unwinds, they must ALL match the translation code 
     446                    int match = 0; 
     447                    EXCEPTION_RECORD * er = exceptionRecord; 
     448                    do 
    372449                    { 
    373                         // This code must match the translation code 
    374                         if (exceptionRecord.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) 
     450                        if (!ci) 
    375451                        { 
    376                             // printf("ei[0] = %p\n", exceptionRecord.ExceptionInformation[0]); 
    377                             ci = (**(cast(ClassInfo **)(exceptionRecord.ExceptionInformation[0]))); 
     452                            // This code must match the translation code 
     453                            if (er.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) 
     454                            { 
     455                                 // printf("ei[0] = %p\n", exceptionRecord.ExceptionInformation[0]); 
     456                                ci = (**(cast(ClassInfo **)(er.ExceptionInformation[0]))); 
     457                            } 
     458                            else 
     459                                ci = Throwable.typeinfo; 
    378460                        } 
    379                         else 
    380                             ci = Throwable.typeinfo; 
    381                     } 
    382                     if (_d_isbaseof(ci, pcb.type)) 
     461                        match = _d_isbaseof(ci, pcb.type); 
     462                        // We need to check all the collateral exceptions 
     463                        if (er.ExceptionFlags & EXCEPTION_COLLATERAL) { 
     464                            // Walk to the next collateral exception. 
     465                            if (er.ExceptionRecord) 
     466                                er = er.ExceptionRecord; 
     467                            else // It is collateral for an existing exception chain 
     468                                 // for which we've already found the catch{}. It is 
     469                                 // possible that the new collateral makes the old catch 
     470                                 // invalid. 
     471                                er = inflightExceptionList; 
     472                            ci = null; 
     473                        } 
     474                        else break; // if no collateral exceptions, we're done. 
     475                    } while(match); 
     476 
     477                    if (match) 
    383478                    { 
    384                         // Matched the catch type, so we've found the handler. 
     479                        // Matched the catch type, so we've found the catch handler 
     480                        // for this exception. 
     481                        // We need to add this exception to the list of in-flight 
     482                        // exceptions, in case something collides with it. 
     483                        EXCEPTION_RECORD * originalException = skipCollateralExceptions(exceptionRecord); 
     484                        if (originalException.ExceptionRecord is null 
     485                            && !(exceptionRecord is inflightExceptionList)) 
     486                        { 
     487                            originalException.ExceptionRecord = inflightExceptionList; 
     488                        } 
     489 
     490                        inflightExceptionList = exceptionRecord; 
     491 
     492                        // Have system call all finally blocks in intervening frames 
     493                        _d_global_unwind(frame, exceptionRecord); 
     494 
     495                        // Call all the finally blocks skipped in this frame 
     496                        _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 
     497 
     498                        frame.table_index = prev_ndx;  // we are out of this handler 
     499 
     500                        // Now create the D exception from the SEH exception record chain. 
     501                        // Note that since non-Throwables are throwable(!), the chain might be incomplete. 
     502                        EXCEPTION_RECORD * z = exceptionRecord; 
     503                        Throwable prev = null; 
     504                        while (z && (z.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     505                        { 
     506                            Throwable w = cast(Throwable)_d_translate_se_to_d_exception(z); 
     507                            if (w) // if throwable 
     508                            { 
     509                                w.next = prev; 
     510                                prev = w; 
     511                            } 
     512                            z = z.ExceptionRecord; 
     513                        } 
     514                        pti = _d_translate_se_to_d_exception(z); 
     515                        if (cast(Throwable)pti) 
     516                            (cast(Throwable)pti).next = prev; 
     517                        // else it wasn't throwable. (This happens in the test suite eh2.d, test1) 
     518 
     519                        // Pop the exception from the list of in-flight exceptions 
     520                        inflightExceptionList = z.ExceptionRecord; 
     521 
    385522                        int regebp; 
    386  
    387                         pti = _d_translate_se_to_d_exception(exceptionRecord); 
    388  
    389523                        // Initialize catch variable 
    390524                        regebp = cast(int)&frame.ebp;              // EBP for this frame 
    391525                        *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 
    392  
    393                         _d_setUnhandled(pti); 
    394                          
    395                         // Have system call all finally blocks in intervening frames 
    396                         _d_global_unwind(frame, exceptionRecord); 
    397                          
    398                         // Call all the finally blocks skipped in this frame 
    399                         _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 
    400  
    401                         _d_setUnhandled(null); 
    402  
    403                         frame.table_index = prev_ndx;  // we are out of this handler 
    404526 
    405527                        // Jump to catch block. Does not return. 
    406528                        { 
    407529                            uint catch_esp; 
    408530                            alias void function() fp_t; // generic function pointer 
    409531                            fp_t catch_addr = cast(fp_t)(pcb.code); 
    410532                            catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof; 
    411533                            asm 
    412534                            { 
    413535                                mov     EAX,catch_esp; 
     
    576698So, we need to restart from 'frame.prev'. 
    577699*/ 
    578700 
    579701extern (C) 
    580702EXCEPTION_DISPOSITION searchCollisionExceptionHandler( 
    581703            EXCEPTION_RECORD *exceptionRecord, 
    582704            DEstablisherFrame *frame, 
    583705            CONTEXT *context, 
    584706            void *dispatcherContext) 
    585707{ 
    586     if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))  
     708    if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) 
     709    { 
     710        // Mark this as a collateral exception 
     711        EXCEPTION_RECORD * n = skipCollateralExceptions(exceptionRecord); 
     712        n.ExceptionFlags |= EXCEPTION_COLLATERAL; 
     713 
    587714        return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
    588      
     715    } 
     716 
    589717    // An exception has been thrown during unwinding. 
    590718    // It happened during the SEARCH phase. 
    591719    // We need to cancel the original search pass, so we'll restart from 'frame'. 
    592720    *(cast(void **)dispatcherContext) = frame; 
    593721    return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 
    594722} 
    595723 
    596724extern(C) 
    597725EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( 
    598726            EXCEPTION_RECORD *exceptionRecord, 
    599727            DEstablisherFrame *frame, 
    600728            CONTEXT *context, 
    601729            void *dispatcherContext) 
    602730{ 
    603     if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))  
     731    if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) 
     732    { 
     733        // Mark this as a collateral exception 
     734        EXCEPTION_RECORD * n = skipCollateralExceptions(exceptionRecord); 
     735        n.ExceptionFlags |= EXCEPTION_COLLATERAL; 
    604736        return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
    605      
     737    } 
    606738    // An exception has been thrown during unwinding. 
    607739    // It happened during the UNWIND phase. 
    608740    // We need to cancel the unwind pass, AND we need to cancel the search 
    609741    // pass that initiated the unwind. So, we need to restart from 'frame.prev'. 
    610742    *(cast(void **)dispatcherContext) = frame.prev; 
    611743    return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 
    612744} 
    613745 
    614746/************************************** 
    615747 * Call finally blocks in the current stack frame until stop_index.