313
edits
m (fixed header levels) |
(edit & wikify s3) |
||
Line 83: | Line 83: | ||
} except (nserror e) {} | } except (nserror e) {} | ||
== Making call sites exception safe == | |||
Once the nothrows are identified (and optionally, call sites that ignore errors are rewritten), we are left with the harder job of rewriting all other call sites to be exception safe. | |||
We'll start with the assumption that call sites adhere to a simple pattern that's relatively easy to rewrite, namely: | |||
nsresult rv = callSomeMethod(); | |||
// note: no code between call & rv test | |||
// note: also accept NS_SUCCEEDED(rv) with code in opposite then/else branches | |||
if (NS_FAILED(rv)) { | |||
maybe fixup(); | |||
return rv OR return NS_ERROR_FOO or return 0; | |||
} else { | |||
maybe do_stuff(); | |||
} | |||
This requires a static analysis that finds all call sites that don't match this pattern so they can be flagged for manual rewrite. | |||
Given the simple pattern, the rewrite looks like this: | |||
try { | try { | ||
Line 113: | Line 108: | ||
} except (nserror e) { | } except (nserror e) { | ||
maybe fixup(); | maybe fixup(); | ||
maybe throw NS_ERROR_FOO; | maybe throw NS_ERROR_FOO; // if original returns new error code | ||
maybe return; | maybe return; // if original returns 0 | ||
} | } | ||
But there are at least two big practical problems with this rewrite. | |||
First, when the original code failure case ends with 'return rv', the natural thing to do with exceptions is let the exception propagate. (If we use the rewrite above, we're cutting off the stack trace, which is bad for debugging.) If there is no 'fixup' code, then this is easy: just have no try block. If there is 'fixup' code, then we should rewrite to use a compensator object: | |||
// class created to run fixup | |||
class SomeCompensator { | |||
~SomeCompensator() { fixup(); } | |||
}; | |||
// call site | |||
SomeCompensator(); | |||
callSomeMethod(); | |||
This is the style recommended by C++ authorities. The problem here is that the code is no longer equivalent to the original nsresult code: the fixup is run on success as well as failure. We still might be able to produce this rewriting automatically, but we need to know more about how the fixups appear in our code and how they can be identified. | |||
The second problem is that the if condition is often a little more complicated than what our pattern allows. We also have: | |||
* NS_FAILED(rv) || foo | |||
* NS_FAILED(rv) && foo | |||
* NS_FAILED(rv1) || NS_FAILED(rv2) | |||
* NS_FAILED(rv1) && NS_SUCCEEDED(rv2) | |||
Above, we said we'd flag these for manual fix, but some of them, like the first two, are pretty common and we might want to fix them automatically. The first two cases seem fairly easy to handle, but we need more information on how they're being used. |
edits