Exceptions: Difference between revisions

1,085 bytes added ,  17 January 2008
Line 35: Line 35:
Call sites of <code>nsresult</code> methods need to be updated to handle exceptions instead of testing error codes. The key is to preserve current behavior. Call sites fall into a few categories:
Call sites of <code>nsresult</code> methods need to be updated to handle exceptions instead of testing error codes. The key is to preserve current behavior. Call sites fall into a few categories:


I'''gnore <code>nsresult</code>.''' Call sites that ignore the <code>nsresult</code> must be wrapped in <code>try { call() } catch {}</code>. If we can prove that the call will never throw an exception, which we can currently do for about 1/3 of this category, we could remove the wrapper. However, this is potentially dangerous for the future: the called method may someday throw or propagate exceptions. Thus, it is better to make the call site exception-safe before removing the wrapper.
'''Ignore <code>nsresult</code>.''' Call sites that ignore the <code>nsresult</code> must be wrapped in <code>try { call() } catch {}</code>. If we can prove that the call will never throw an exception, which we can currently do for about 1/3 of this category, we could remove the wrapper. However, this is potentially dangerous for the future: the called method may someday throw or propagate exceptions. Thus, it is better to make the call site exception-safe before removing the wrapper.


* Forward <code>nsresult</code>. This is when the method containing the call site simply propagates the error code to its caller without executing any other code (except destructors). The macro <code>NS_ENSURE_SUCCESS</code> does this. This category is very nice: with exceptions, there is no error handling code at all. To remove the existing error handling code, we can simply remove the assignment of the error code out of the call site and remove the return statement. This may create a dead <code>rv</code> variable, which can be deleted as a (conceptual) separate step. Note: we place call sites that log the error into this category as well: logging will be needed less with exceptions.
'''Forward <code>nsresult</code>.''' This is when the method containing the call site simply propagates the error code to its caller without executing any other code (except destructors). The macro <code>NS_ENSURE_SUCCESS</code> does this. This category is very nice: with exceptions, there is no error handling code at all. To remove the existing error handling code, we can simply remove the assignment of the error code out of the call site and remove the return statement. This may create a dead <code>rv</code> variable, which can be deleted as a (conceptual) separate step.  


* Compensate, then forward <code>nsresult</code>. This is when the method containing the call site propagates the error code, but only after running compensation code. Compensation code restores some invariant that would otherwise be lost. The prototypical example is releasing resources to avoid leaks. The preferred (by the designers of C++) pattern for this scenario is to create a stack object whose destructor maintains the invarant.  
We place call sites that log the error into this category as well: logging will be needed less with exceptions.


Call sites that simply forward error codes to their caller need no error handling.
'''Compensate, then forward <code>nsresult</code>.''' This is when the method containing the call site propagates the error code, but only after running compensation code. Compensation code restores some invariant that would otherwise be lost. The prototypical example is releasing resources to avoid leaks.  


Call sites that test for errors and run fixup code will need to be either (a) wrapped in a try block with a catch block containing the fixup code or (b) preceded by stack allocation of a http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization RAII] object in an appropriate scope.
The preferred (by the designers of C++) pattern for this scenario is to create a stack object whose destructor maintains the invariant. This is particularly easy to do if the <code>nsresult</code> compensation code executes for both the failure and success cases: we wrap the compensation code in an object destructor and define an instance just before the call site.
 
If the compensation code currently runs only in the error case, then a direct translation cannot be made using compensation objects. The direct translation would be <code>try { call() } catch { compensate(); throw e; }</code>. However, it may be that for some call sites it is possible to express the error handling code using a stack compensation object.
 
'''Compensate, then return other <code>nsresult</code>.''' This is just like the previous case, except that a different error code is returned. In this case, we must use a catch block so that we can throw a new exception--propagation won't preserve the existing behavior. But we could still use stack compensation objects for compensation, and place only the new <code>throw</code> in the catch block.
 
But we might prefer to propagate the exception--the error handling code is simpler, and the stack trace is preserved. This would require more changes in the calling code.
 
'''Compensate, then continue.''' This is like the previous case, except that no error code is returned--execution continues. Again, because we are not propagating the exception, we need a catch block. Again, we do have the option of using stack compensation objects and an empty catch block.


=== Exceptions for OOM ===
=== Exceptions for OOM ===
313

edits