Changeset 509

Show
Ignore:
Timestamp:
01/14/11 19:46:57 (1 year ago)
Author:
Don Clugston
Message:

Improvement to Exception Chaining: When an exception is thrown and not caught before a previously thrown exception is caught, the later exception is chained to the previous one via the 'next' member of Throwable. The original exception must be caught, and this results in the capture of the entire chain.
Thrown objects derived from Error are treated differently. They bypass the normal chaining mechanism, such that the chain can only be caught by catching the first Error.
A new member of Error, 'bypassedException', points to the original exception (the head of the chain) if a bypass occurred, otherwise it is null.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/import/object.di

    r491 r509  
    326326    this(string msg, Throwable next = null); 
    327327    this(string msg, string file, size_t line, Throwable next = null); 
     328    Throwable   bypassedException; 
    328329} 
    329330 
  • trunk/src/object_.d

    r491 r509  
    13411341    { 
    13421342        super(msg, next); 
     1343        bypassedException = null; 
    13431344    } 
    13441345 
     
    13461347    { 
    13471348        super(msg, file, line, next); 
    1348     } 
     1349        bypassedException = null; 
     1350    } 
     1351    /// The first Exception which was bypassed when this Error was thrown, 
     1352    /// or null if no Exceptions were pending. 
     1353    Throwable   bypassedException; 
    13491354} 
    13501355 
  • trunk/src/rt/deh.d

    r503 r509  
    351351 * exception, having its EXCEPTION_COLLATERAL bit set, or else will be caught. 
    352352 * If it is caught, a D exception object is created, containing all of the 
    353  * collateral exceptions, 
     353 * collateral exceptions. 
    354354 * 
    355355 * There are many subtleties in this design: 
     
    363363 * So we cannot store the collision information in the exception record. 
    364364 * (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: 
    376365 */ 
    377366 
     
    424413        DCatchBlock *pcb; 
    425414        uint ncatches;              // number of catches in the current handler 
    426         Object pti; 
    427         ClassInfo ci; 
    428  
    429         ci = null;                      // only compute it if we need it 
     415         
     416        /* The Master or Boss exception controls which catch() clause will  
     417         * catch the exception. If all collateral exceptions are derived from 
     418         * Exception, the boss is the first exception thrown. Otherwise, 
     419         * the first Error is the boss. 
     420         * But, if an Error (or non-Exception Throwable) is thrown as a collateral 
     421         * exception, it will take priority over an Exception. 
     422         */ 
     423        EXCEPTION_RECORD * master = null; // The Master exception. 
     424        ClassInfo masterClassInfo;       // Class info of the Master exception. 
     425         
     426        masterClassInfo = null;           // only compute it if we need it 
    430427 
    431428        // walk through handler table, checking each handler 
     
    440437                pci = cast(DCatchInfo *)(cast(ubyte *)handlerTable + phi.cioffset); 
    441438                ncatches = pci.ncatches; 
     439                 
    442440                for (i = 0; i < ncatches; i++) 
    443441                { 
    444442                    pcb = &pci.catch_block[i]; 
    445                     // If there are collided unwinds, they must ALL match the translation code 
    446443                    int match = 0; 
    447444                    EXCEPTION_RECORD * er = exceptionRecord; 
    448                     do 
     445                    // We need to check all the collateral exceptions. 
     446                    for(;;) 
    449447                    { 
    450                         if (!ci
     448                        if (er.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION
    451449                        { 
    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; 
     450                            // printf("ei[0] = %p\n", er.ExceptionInformation[0]); 
     451                            ClassInfo ci = (**(cast(ClassInfo **)(er.ExceptionInformation[0]))); 
     452                            // If we've reached the oldest exception without 
     453                            // finding an Error, this one must be the master. 
     454                            if (!master && !(er.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     455                            {   
     456                                master = er; 
     457                                masterClassInfo = ci; 
     458                                break; 
     459                            }                              
     460                            if (_d_isbaseof(ci, Error.classinfo)) 
     461                            {   // It's derived from Error. This _may_ be the master. 
     462                                master = er; 
     463                                masterClassInfo = ci; 
     464                            } // Else it's a collateral Exception 
    460465                        } 
    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; 
     466                        else 
     467                        {   // Non-D exception. It will become an Error. 
     468                            masterClassInfo = Error.typeinfo; 
     469                            master = er; 
    473470                        } 
    474                         else break; // if no collateral exceptions, we're done. 
    475                     } while(match); 
    476  
    477                     if (match) 
     471                        // End the loop if this was the original exception 
     472                        if (! (er.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     473                            break; 
     474                         
     475                        // Now get the next collateral exception. 
     476                        if (er.ExceptionRecord) 
     477                            er = er.ExceptionRecord; 
     478                        else // It is collateral for an existing exception chain 
     479                             // for which we've already found the catch{}. It is 
     480                             // possible that the new collateral makes the old catch 
     481                             // invalid. 
     482                            er = inflightExceptionList; 
     483                    } 
     484                    if (_d_isbaseof(masterClassInfo, pcb.type)) 
    478485                    { 
    479                         // Matched the catch type, so we've found the catch handler 
    480                         // for this exception. 
     486                        // Matched the catch type, so we've found the catch 
     487                        // handler for this exception. 
     488                        // BEWARE: We don't yet know if the catch handler will 
     489                        // actually be executed. If there's an unwind collision, 
     490                        // this call may be abandoned: the calls to  
     491                        // _global_unwind and _local_unwind may never return, 
     492                        // and the contents of the local variables will be lost. 
     493                                                 
    481494                        // We need to add this exception to the list of in-flight 
    482495                        // exceptions, in case something collides with it. 
     
    487500                            originalException.ExceptionRecord = inflightExceptionList; 
    488501                        } 
    489  
    490502                        inflightExceptionList = exceptionRecord; 
    491503 
     
    495507                        // Call all the finally blocks skipped in this frame 
    496508                        _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 
    497  
     509                         
     510                         
    498511                        frame.table_index = prev_ndx;  // we are out of this handler 
    499  
     512                         
    500513                        // Now create the D exception from the SEH exception record chain. 
    501                         // Note that since non-Throwables are throwable(!), the chain might be incomplete. 
    502514                        EXCEPTION_RECORD * z = exceptionRecord; 
    503515                        Throwable prev = null; 
    504                         while (z && (z.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     516                        Error masterError = null; 
     517                        Throwable pti; 
     518                         
     519                        for(;;) 
    505520                        { 
    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; 
     521                            Throwable w = _d_translate_se_to_d_exception(z); 
     522                            if (z == master && (z.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     523                            {   // if it is a short-circuit master, save it 
     524                                masterError = cast(Error)w; 
    511525                            } 
     526                            Throwable a = w; 
     527                            while (a.next) 
     528                                a = a.next; 
     529                            a.next = prev; 
     530                            prev = w; 
     531                            if (!(z.ExceptionFlags & EXCEPTION_COLLATERAL)) 
     532                                break; 
    512533                            z = z.ExceptionRecord; 
    513534                        } 
    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  
     535                        // Reached the end. Now add the Master, if any.                                    
     536                        if (masterError) 
     537                        { 
     538                            masterError.bypassedException = prev; 
     539                            pti = masterError; 
     540                        } 
     541                        else 
     542                        { 
     543                            pti = prev; 
     544                        } 
    519545                        // Pop the exception from the list of in-flight exceptions 
    520546                        inflightExceptionList = z.ExceptionRecord; 
     
    568594void _d_throwc(Object h) 
    569595{ 
     596    // @@@ TODO @@@ Signature should change: h will always be a Throwable. 
     597     
    570598    //printf("_d_throw(h = %p, &h = %p)\n", h, &h); 
    571599    //printf("\tvptr = %p\n", *(void **)h); 
     
    578606 
    579607/*********************************** 
    580  * Converts a Windows Structured Exception code to a D Exception Object. 
    581  */ 
    582  
    583 Object _d_translate_se_to_d_exception(EXCEPTION_RECORD *exceptionRecord) 
    584 { 
    585     Object pti; 
     608 * Converts a Windows Structured Exception code to a D Throwable Object. 
     609 */ 
     610 
     611Throwable _d_translate_se_to_d_exception(EXCEPTION_RECORD *exceptionRecord) 
     612{ 
     613    Throwable pti; 
    586614   // BUG: what if _d_newclass() throws an out of memory exception? 
    587615 
     
    589617        case STATUS_DIGITAL_MARS_D_EXCEPTION: 
    590618            // Generated D exception 
    591             pti = cast(Object)cast(void *)(exceptionRecord.ExceptionInformation[0]); 
     619            pti = cast(Throwable)cast(void *)(exceptionRecord.ExceptionInformation[0]); 
    592620            break; 
    593621