Exceptional Circumstances

My previous post on outparam rewriting described the wealth of functions that can be rewritten. Unfortunately, most functions in Mozilla are declared in XPIDL interfaces.

I have been convinced that my plan to rewrite xpidlgen to avoid outparameters wont be possible because most XPIDLinterfaces can be implemented by JavaScript in a few different ways. That is problematic because in addition to return values, JavaScript can also have an exception thrown at any point and have that converted to an nsresult error code by XPConnect. That means that the getters implemented in JavaScript are not in the set of functions that only return NS_OK+outparam/someerror. I wouldn’t be at all disappointed if someone proved me wrong here.

nsexception

There is one other way to rid the code of outparameters (including getter_AddRefs and friends). Time to face my greatest reluctance: rewriting Mozilla to use exceptions. Brendan has been talking about it for a long time, but I have been skeptical until now, mostly due to the complexity of rewriting that much code. However, I have more confidence in rewriting huge amounts of code now since the XPCOMGC rewrite which touched most functions in Mozilla without too much trouble (in relative terms).

Motivation

There are some obvious benefits to be gained from switching to exceptions other than a reduction in code-size (and footprint?) and having code that looks more like common C++.

We would like to modify tamarin to use C++ exceptions such that an exception thrown from JavaScript would unroll the mixed C++/JS stack. This would simplify and enable significant optimizations for XPConnect.

I am dreaming of JITed marshaling code for C++->JS calls and having a low level FFI interface(ie being able to call most C/C++ methods directly) on the JavaScript side such that tracing JIT could automatically optimize common XPConnect calls. This an exciting area and there are lots of details to be worked out, so I’d love to see some feedback (or better yet proof of concept code!) on this.

The Plan – Rewriting

I have started implementing thrower (I am not a great namer), a tool for converting various code patterns involving nsresult into something that uses an nsexception wrapper.

Since this rewrite requires a lot more scanning for code patterns I added an elsa feature to allow pattern matching on AST nodes in C++ (also using exceptions). Since there are lot of patterns to transform, for documentation I will be writing many minimal testcases documenting(and testing) exactly what gets rewritten. Any interested parties are welcome to contribute Mozilla error handling patterns as testcases.

Verifying the Result

Just like in the XPCOMGC rewrite, code will have to be scanned to verify that it fits in the “new world order”. Unlike XPCOMGC, there are additional flow-sensitive issues to scan for to ensure that the code is thread-safe. The scans are at a lower level than dehydra currently works at, so it’s a perfect opportunity to either extend dehydra or write the new tool.

It would be especially cool to implement the code analysis tool as a gcc plugin.  Sean Callanan’s “Extending GCC with Modular GIMPLE Optimizations” paper in the GCC summit proceedings should be an excellent starting point.

This is an exciting experiment. I look forward to reducing speculation on the risks/benefits of switching the codebase to use exceptions with some concrete data.

9 comments

  1. Robert O'Callahan

    > Unfortunately, most functions in Mozilla are
    > declared in XPIDL interfaces.

    Fortunately, this is an overstatement.

    I think we need to mark a whole lot of interfaces as “script-callable, but not implementable by script”. That would make address a lot of your problems right away and would also solve some other problems. I need to blog about this.

  2. s/thrower/dartboard/

    Another item I should point out is XPCOM success codes other than NS_OK, like NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA. Your rewriting could have two levels of problems with that. In C++, would you really want to throw a success code? In JS, we use Components.returnCode (well, we should, anyway) – how would that fit into the revised XPCOM model, going from JS to C++?

    Also, could I bribe you into forcing JS components to use nsIException and/or Components.Exception more?

  3. Mark Banner (standard8)

    Would this mean we could loose the binary compatibility that Benjamin’s been muttering about?

    If so, I don’t think we can accept this unless we are guaranteed to have a good c/c++ bridge to external libraries accessible from js.

  4. Taras: there’s close to zero value in the JS exception that’s pending in a cx being mapped to some non-OK nsresult. Please do not consider XPConnect or the JS binding to XPIDL sacrosanct. Break it in order to outparamdel and exception-ize!

    Mark: Mozilla 2 breaks binary compat as well as source API compat.

    /be

  5. Although as I mentioned on a recent call, there is huge value in an analysis + a better scheme that avoids JS exceptions left pending when C++ suppresses (rightly or wrongly) a failure result by returning ok.

    /be

  6. My comment 4 was geared at decoupling outparamdel work from exception work, in case it wasn’t clear. If JS exceptions throw as C++ and vice versa, cool. If we could outparamdel faster before exceptionizing that would be good too. The pending JS exception is kept in the JSContext, essentially part of thread-local storage. No need to pass it up the stack as a return value, but we do need a failure return value that is not overloaded (null, -1, etc.).

    Not sure this is worth it, but I thought I would mention it (again).

    /be

  7. I just wanted to point one thing about the decision to use exceptions in C++ : If you want to use exceptions, then all the involved code *must* be perfectly correct RAII.

    That’s the one sentence lesson to get from the “Cleaner, more elegant, and harder to recognize” exception rant, http://blogs.msdn.com/oldnewthing/archive/2005/01/14/352949.aspx , only proper RAII code continues to work properly and does not unexpectedly break when you introduce exceptions.

    Note that RAII code is a good thing in itself, converting the codebase to it would be a great thing, and the fact it makes exception “work” is only an added bonus.

  8. I think we need to do a lot of massaging on the C++ code first, in order to make the final pass over the code to turn on exceptions less daunting.

    First of Alex mention one problem, non-NS_OK success values. We need to abolish these from the tree. I suspect this will have to be done manually since the cure will vary. There are also not that many of these so it shouldn’t be too hard to do by hand.

    The next thing is to try to reduce the amount of code we currently have with calling patterns like these:

    int foo = 0;
    CallXPIDLFunc(&foo);
    if (foo > 4) {…

    I.e. code that ignores the potential error code from an XPIDL call. These can be one of three things:

    1. In many many cases the called function will never throw (or will only throw in out-of-memory situations).
    2. Most other cases these callsites are just plain wrong and does need to be fixed to forward the error.
    3. In a rare few cases the code is intentionally ignoring the error and basically does an implicit try/catch around the call.

    If we could automatically eliminate 1 using automated tools we would have a much clearer picture of how to handle 2 vs 3 I think.

    We could fix 1 by other using rocs suggestion, or by saying that non-throwing functions implemented in js should have any exceptions logged and ignored. Or something like it.

    One way to separate 2 vs. 3 once 1 is taken care of would be to automatically write a patch that turns ignored return values into try/catches, and then we can manually go through the patch and remove the try/catches for the callers that fall into 2.

  9. Jonas,
    If you look at the sample patch attached to today’s blog entry I already flag cases that ignore errors.