Updating UUIDs (the wrong way)

When you change an IDL interface, you have to update its uuid. Simple enough, just grab a new uuid and stick it in the .idl file. Easy enough, right?

I’ve been working on JSD (the JavaScript debugger interface) recently, and its IDL file contains 18 different interfaces. So say I add a method to jsdIScript. What interfaces do I need to update? From empirical observations (as in, when I forget to do one I get yelled at by a reviewer), you have to update the uuid on the interface containing any added methods, any interfaces that use that interface within their definitions, and any interface that… inherits? extends? that interface. Recursively.

Update: No you don’t, says #developers, though currently the conversation is not finished and it seems like it may come down to something of a judgement call (something like “if you modify the vtable, update the uuid. If you change an interface used in a method in such a way that it might break a user, update the uuid.”) I’ve updated my script to use the more common rule (inheritance only), though if you want the stricter behavior you can get it with –mode=params.

Leaving the rest of this post here for now:


Solution 1: Manually go through and trace the dependencies, updating uuids as you go.

Bleckth. Maybe someone else can manage to do that properly, but I’m an airhead, and I’d never get that exactly right.

Solution 2: Nuke ’em all and let God sort ’em out. (As in, update every single uuid in the file.)

Easy, but inelegant and I sometimes wonder if knowing whether an interface changed might actually matter to someone. Besides, this could result in me getting yelled at by a reviewer, and that’s what I depend on for figuring out the rules (see “empirical observations”, above.)

Solution 3: automate solution 1.

…so I did.

Give it a .idl file to chew on and one or more interfaces that you know you’ve changed, and it’ll chase through the dependencies for you. Assuming I got the rules right. It spits out a new file with brand-new uuids on all affected interfaces, and even spews to stderr the set of interfaces it’s updating and why:

% update-uuids jsdIDebuggerService.idl jsdIContext >/dev/null
uuids to update:
 jsdIContext because it was given on command line
   3e5c934d-6863-4d81-96f5-76a3b962fc2b -> 24ad10b2-8b4f-49f6-9236-f0ecaed0e19a
 jsdIStackFrame because its body contains jsdIContext
   7c95422c-7579-4a6f-8ef7-e5b391552ee5 -> 4c8c5902-77e8-4a9d-99f8-0ae6f0c58eec
 jsdIContextEnumerator because its body contains jsdIContext
   57d18286-550c-4ca9-ac33-56f12ebba91e -> d102ff63-59ea-4ed7-86d5-490c9e9b6b5a
 jsdICallHook because its body contains jsdIStackFrame
   3eff1314-7ae3-4cf8-833b-c33c24a55633 -> 150610f5-89cd-4f76-b960-06447471eb00
 jsdIExecutionHook because its body contains jsdIStackFrame
   3a722496-9d78-4f0a-a797-293d9e8cb8d2 -> cd3bfe98-c8a3-4c98-91d1-c7e2e79c396c
 jsdIDebuggerService because its body contains jsdIContextEnumerator
   aa232c7f-855f-4488-a92c-6f89adc668cc -> 75ab47da-2400-4efe-bb5e-745dceba4e06

(Sorry, no examples of inheritance-triggered changes there.)

It’s a quickie parse-with-regexes Perl hack.

One major flaw — it only considers a single .idl file. If some other .idl file depends on an interface modified within your file, then this won’t tell you it needs to be updated. I’m pretty sure that no other IDLs depend on the JSD IDL, so I don’t care yet. If this would be useful to you and that’s a necessary feature, let me know and I’ll throw it in. It’s easy enough to implement as long as you provide the list of IDL files to consider.

The other major flaw is that this doesn’t update the uuids in header files, once again because JSD didn’t need it. That would be some more work, and I don’t even know if I have the rules right so I’m not going to bother unless someone asks me to and tells me this is the right thing in the first place.

If nobody comments here within a week or so telling me I’m completely wrong about how this stuff works, I’ll add a link to this script on MDC. Maybe. If I remember. (And it turns out, someone did tell me I’m completely wrong. Yay!)

Tags: , ,

3 comments

  1. One thing that’s always confused me – why must you update the uuid when the interface changes?

  2. @ToddW: It’s for the sake of binary code that might be using this interface (extensions or XULRunner applications with binary components, NPAPI plugins and such). Binary code is compiled with a particular version of Gecko SDK and expects the interface to match that version. If you use it with a Firefox/XULRunner version where this interface is different but UUID is the same you will get a crash, maybe even a security issue. If however UUID changed the binary component will request the interface with a “wrong” id – and get NS_ERROR_NO_INTERFACE back. It will hopefully fail gracefully then, disaster averted.

  3. Binary compatibility is the main reason, but the question remains whether it is necessary or desirable to update the UUID even if the binary interface doesn’t require it. Say I add a hasHead() method to nsIChicken because I now allow nsIChickens to be either alive or dead, where before they could only be alive. Then clearly I have to update the nsIChicken UUID for binary compatibility. But what about nsIChickenChoir? It has not been modified, but its enlistMember(nsIChicken) method has changed semantics somewhat, and when nsIChickenChoir.sing() is invoked, the results may be somewhat nonsensical with an older implementation. Should its UUID be updated? Does it depend on whether the internal implementation of objects implementing nsIChickenChoir had to change? Binary users won’t crash, but it might be better for them to fail to get an nsIChickenChoir handle rather than unexpectedly confronting concert-goers with a choir containing dead chickens. (Perhaps a protocol handler that adds support for a different protocol, or removes support for a protocol, might be a better example.) Then again, do non-binary components already have this problem? Do they do a dynamic name-based lookup or something?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.