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

Changeset 509

Show
Ignore:
Timestamp:
01/15/11 00:46:57 (14 years 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  
    318318{ 
    319319    this(string msg, Throwable next = null); 
    320320    this(string msg, string file, size_t line, Throwable next = null); 
    321321} 
    322322 
    323323 
    324324class Error : Throwable 
    325325{ 
    326326    this(string msg, Throwable next = null); 
    327327    this(string msg, string file, size_t line, Throwable next = null); 
     328    Throwable   bypassedException; 
    328329} 
    329330 
    330331extern (C) 
    331332{ 
    332333    // from druntime/src/compiler/dmd/aaA.d 
    333334 
    334335    size_t _aaLen(void* p); 
    335336    void*  _aaGet(void** pp, TypeInfo keyti, size_t valuesize, ...); 
    336337    void*  _aaGetRvalue(void* p, TypeInfo keyti, size_t valuesize, ...); 
    337338    void*  _aaIn(void* p, TypeInfo keyti); 
  • trunk/src/object_.d

    r491 r509  
    13331333        super(msg, file, line, next); 
    13341334    } 
    13351335} 
    13361336 
    13371337 
    13381338class Error : Throwable 
    13391339{ 
    13401340    this(string msg, Throwable next = null) 
    13411341    { 
    13421342        super(msg, next); 
     1343        bypassedException = null; 
    13431344    } 
    13441345 
    13451346    this(string msg, string file, size_t line, Throwable next = null) 
    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 
    13511356 
    13521357/////////////////////////////////////////////////////////////////////////////// 
    13531358// ModuleInfo 
    13541359/////////////////////////////////////////////////////////////////////////////// 
    13551360 
    13561361 
    13571362enum 
    13581363{ 
  • trunk/src/rt/deh.d

    r503 r509  
    343343 * The 'ExceptionRecord' member of the EXCEPTION_RECORD struct is used to 
    344344 * store a link to the earlier member on the list. 
    345345 * All exceptions which have found their catch handler are linked into this 
    346346 * list. The exceptions which collided are marked by setting a bit in the 
    347347 * ExceptionFlags. I've called this bit EXCEPTION_COLLATERAL. It has never 
    348348 * been used by Microsoft. 
    349349 * 
    350350 * Every member of the list will either eventually collide with the next earlier 
    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: 
    356356 * (1) The exception records are all on the stack, so it's not possible to 
    357357 * modify them very much. In particular, we have very little choice about how 
    358358 * unwinding works, so we have to leave all the exception records essentially 
    359359 * intact. 
    360360 * (2) The length of an exception record is not constant. System exceptions 
    361361 * are shorter than D exceptions, for example. 
    362362 * (3) System exceptions don't have any space for a pointer to a D object. 
    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 
    378367EXCEPTION_RECORD * inflightExceptionList = null; 
    379368 
    380369/*********************************** 
    381370 * Find the first non-collateral exception in the list. If the last 
    382371 * entry in the list has the EXCEPTION_COLLATERAL bit set, it means 
    383372 * that this fragment will collide with the top exception in the 
    384373 * inflightException list. 
    385374 */ 
     
    416405         _d_local_unwind(handlerTable, frame, -1, &unwindCollisionExceptionHandler); 
    417406    } 
    418407    else 
    419408    { 
    420409        // Jump to catch block if matching one is found 
    421410        int ndx,prev_ndx,i; 
    422411        DHandlerInfo *phi; 
    423412        DCatchInfo *pci; 
    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 
    432429        // with an index smaller than the current table_index 
    433430        for (ndx = frame.table_index; ndx != -1; ndx = prev_ndx) 
    434431        { 
    435432            phi = &handlerTable.handler_info[ndx]; 
    436433            prev_ndx = phi.prev_index; 
    437434            if (phi.cioffset) 
    438435            { 
    439436                // this is a catch handler (no finally) 
    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. 
    483496                        EXCEPTION_RECORD * originalException = skipCollateralExceptions(exceptionRecord); 
    484497                        if (originalException.ExceptionRecord is null 
    485498                            && !(exceptionRecord is inflightExceptionList)) 
    486499                        { 
    487500                            originalException.ExceptionRecord = inflightExceptionList; 
    488501                        } 
    489502 
    490503                        inflightExceptionList = exceptionRecord; 
    491504 
    492505                        // Have system call all finally blocks in intervening frames 
    493506                        _d_global_unwind(frame, exceptionRecord); 
    494507 
    495508                        // Call all the finally blocks skipped in this frame 
    496509                        _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 
    497  
     509                         
     510                         
    498511                        frame.table_index = prev_ndx;  // we are out of this handler 
    499512 
    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; 
    521547 
    522548                        int regebp; 
    523549                        // Initialize catch variable 
    524550                        regebp = cast(int)&frame.ebp;              // EBP for this frame 
    525551                        *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 
    526552 
    527553                        // Jump to catch block. Does not return. 
    528554                        { 
     
    560586    *exceptionObject = _d_translate_se_to_d_exception(eptrs.ExceptionRecord); 
    561587    return retval; 
    562588} 
    563589 
    564590/*********************************** 
    565591 * Throw a D object. 
    566592 */ 
    567593extern(C) 
    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); 
    572600    _d_createTrace(h); 
    573601    //_d_setUnhandled(h); 
    574602    RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, 
    575603                   EXCEPTION_NONCONTINUABLE, 
    576604                   1, cast(void *)&h); 
    577605} 
    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 
    588616    switch (exceptionRecord.ExceptionCode) { 
    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 
    594622        case STATUS_INTEGER_DIVIDE_BY_ZERO: 
    595623            pti = new Error("Integer Divide by Zero"); 
    596624            break; 
    597625 
    598626        case STATUS_FLOAT_DIVIDE_BY_ZERO: 
    599627            pti = new Error("Float Divide by Zero"); 
    600628            break; 
    601629