add discriminated union types to IPDL
authorChris Jones <jones.chris.g@gmail.com>
Thu, 10 Sep 2009 23:55:03 -0500
changeset 35900 d3e90292fd30307c12f17c7551d10074ab7dc7e7
parent 35899 d0d751ed8c3e82aa203fc1775411b52835728a85
child 35901 fd55e5a42799bb99dab1efff50781c308245a192
push idunknown
push userunknown
push dateunknown
milestone1.9.3a1pre
add discriminated union types to IPDL
dom/plugins/PPluginInstance.ipdl
dom/plugins/PPluginScriptableObject.ipdl
dom/plugins/PluginInstanceChild.h
dom/plugins/PluginMessageUtils.h
dom/plugins/PluginModuleParent.cpp
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
--- a/dom/plugins/PPluginInstance.ipdl
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -41,26 +41,55 @@ include protocol "PPluginModule.ipdl";
 include protocol "PPluginScriptableObject.ipdl";
 include protocol "PBrowserStream.ipdl";
 
 include "mozilla/plugins/PluginMessageUtils.h";
 
 using NPError;
 using NPWindow;
 
+
+// FIXME/bent: demo purposes only
+using mozilla::void_t;
+using mozilla::null_t;
+
+
 namespace mozilla {
 namespace plugins {
 
+
+
+// FIXME/bent: demo purposes only
+union Variant {
+  int;
+  double;
+  void_t;
+  null_t;
+  PPluginInstance;
+};
+
+
+
+
 rpc protocol PPluginInstance
 {
   manager PPluginModule;
 
   manages PPluginScriptableObject;
   manages PBrowserStream;
 
+
+
+  // FIXME/bent: demo purposes only
+child:
+  Test(Variant v1, Variant v2);
+
+
+
+
 child:
   /* NPP_NewStream */
   rpc PBrowserStream(nsCString url,
                      uint32_t length,
                      uint32_t lastmodified,
                      nsCString headers,
                      nsCString mimeType,
                      bool seekable)
--- a/dom/plugins/PPluginScriptableObject.ipdl
+++ b/dom/plugins/PPluginScriptableObject.ipdl
@@ -52,17 +52,16 @@ using nsTArray<NPVariant>;
 namespace mozilla {
 namespace plugins {
 
 rpc protocol PPluginScriptableObject
 {
   manager PPluginInstance;
 
 child:
-
   // NPClass methods
   rpc Invalidate();
 
   rpc HasMethod(NPRemoteIdentifier aId)
     returns (bool aHasMethod);
 
   rpc Invoke(NPRemoteIdentifier aId,
              nsTArray<NPVariant> aArgs)
--- a/dom/plugins/PluginInstanceChild.h
+++ b/dom/plugins/PluginInstanceChild.h
@@ -60,16 +60,72 @@ class PluginInstanceChild : public PPlug
                                              UINT message,
                                              WPARAM wParam,
                                              LPARAM lParam);
 #endif
 
 protected:
     friend class BrowserStreamChild;
 
+
+
+    // FIXME/bent: demo purposes only
+    virtual nsresult
+    RecvTest(const Variant& v1, const Variant& v2)
+    {
+        printf("\n[PluginInstanceChild] v1: ");
+
+        switch (v1.type()) {
+        case Variant::Tint: {
+            int i = v1;
+            printf("variant-int %d", i);
+            break;
+        }            
+        case Variant::Tdouble: {
+            double d = v1;
+            printf("variant-double %e", d);
+            break;
+        }
+        case Variant::TPPluginInstanceChild: {
+            const PPluginInstanceChild* p = v1;
+            printf("plugin instance %p", p);
+            break;
+        }
+        default:
+            NS_RUNTIMEABORT("unexpected Variant value");
+        }
+
+        printf(", v2: ");
+
+        switch (v2.type()) {
+        case Variant::Tint: {
+            int i = v2;
+            printf("variant-int %d", i);
+            break;
+        }            
+        case Variant::Tdouble: {
+            double d = v2;
+            printf("variant-double %e", d);
+            break;
+        }
+        case Variant::TPPluginInstanceChild: {
+            const PPluginInstanceChild* p = v2;
+            printf("plugin instance %p", p);
+            break;
+        }
+        default:
+            NS_RUNTIMEABORT("unexpected Variant value");
+        }
+
+        puts("\n");
+        return NS_OK;
+    }
+
+
+
     virtual nsresult AnswerNPP_SetWindow(const NPWindow& window, NPError* rv);
 
     virtual nsresult AnswerNPP_GetValue(const nsString& key, nsString* value);
 
     virtual PPluginScriptableObjectChild*
     PPluginScriptableObjectConstructor(NPError* _retval);
 
     virtual nsresult
--- a/dom/plugins/PluginMessageUtils.h
+++ b/dom/plugins/PluginMessageUtils.h
@@ -42,16 +42,24 @@
 #include "IPC/IPCMessageUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "nsAutoPtr.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
+
+
+// XXX might want to move these to nscore.h or something, they can be
+// generally useful
+struct void_t { };
+struct null_t { };
+
+
 namespace ipc {
 
 typedef intptr_t NPRemoteIdentifier;
 
 } /* namespace ipc */
 
 namespace plugins {
 
@@ -359,16 +367,42 @@ struct ParamTraits<NPVariant>
       LogParam(NPVARIANT_TO_STRING(aParam), aLog);
       return;
     }
 
     NS_ERROR("Unsupported type!");
   }
 };
 
+template<>
+struct ParamTraits<mozilla::void_t>
+{
+  typedef mozilla::void_t paramType;
+  static void Write(Message* aMsg, const paramType& aParam) { }
+  static bool
+  Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    *aResult = paramType();
+    return true;
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::null_t>
+{
+  typedef mozilla::null_t paramType;
+  static void Write(Message* aMsg, const paramType& aParam) { }
+  static bool
+  Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    *aResult = paramType();
+    return true;
+  }
+};
+
 template <>
 struct ParamTraits<mozilla::plugins::IPCByteRange>
 {
   typedef mozilla::plugins::IPCByteRange paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.offset);
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -183,16 +183,25 @@ PluginModuleParent::NPP_New(NPMIMEType p
         static_cast<PluginInstanceParent*>(
             CallPPluginInstanceConstructor(new PluginInstanceParent(instance,
                                                                     mNPNIface),
                                            nsDependentCString(pluginType), mode,
                                            names,values, &prv)));
     printf ("[PluginModuleParent] %s: got return value %hd\n", __FUNCTION__,
             prv);
 
+
+    // FIXME/bent: demo purposes only
+    Variant v1(0);
+    Variant v2(parentInstance);
+    printf("\n[PluginModuleParent] sending Test msg\n\n");
+    parentInstance->SendTest(v1, v2);
+
+
+
     if (NPERR_NO_ERROR != prv)
         return prv;
     NS_ASSERTION(parentInstance,
                  "if there's no parentInstance, there should be an error");
 
     instance->pdata = (void*) parentInstance.forget();
     return prv;
 }
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -37,28 +37,34 @@ class Visitor:
         raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
             node.__class__.__name__)
 
     def visitTranslationUnit(self, tu):
         for cxxInc in tu.cxxIncludes:
             cxxInc.accept(self)
         for protoInc in tu.protocolIncludes:
             protoInc.accept(self)
+        for union in tu.unions:
+            union.accept(self)
         for using in tu.using:
             using.accept(self)
         tu.protocol.accept(self)
 
     def visitCxxInclude(self, inc):
         pass
 
     def visitProtocolInclude(self, inc):
         # Note: we don't visit the child AST here, because that needs delicate
         # and pass-specific handling
         pass
 
+    def visitUnionDecl(self, union):
+        for t in union.components:
+            t.accept(self)
+
     def visitUsingStmt(self, using):
         pass
 
     def visitProtocol(self, p):
         for namespace in p.namespaces:
             namespace.accept(self)
         if p.manager is not None:
             p.manager.accept(self)
@@ -128,27 +134,43 @@ class Node:
         if visit is None:
             return getattr(visitor, 'defaultVisit')(self)
         return visit(self)
 
     def addAttrs(self, attrsName):
         if not hasattr(self, attrsName):
             setattr(self, attrsName, _struct())
 
+
+class NamespacedNode(Node):
+    def __init__(self, loc=Loc.NONE, name=None):
+        Node.__init__(self, loc)
+        self.name = name
+        self.namespaces = [ ]  
+
+    def addOuterNamespace(self, namespace):
+        self.namespaces.insert(0, namespace)
+
+    def qname(self):
+        return QualifiedId(self.loc, self.name,
+                           [ ns.namespace for ns in self.namespaces ])
+
 class TranslationUnit(Node):
     def __init__(self):
         Node.__init__(self)
         self.filename = None
         self.cxxIncludes = [ ]
         self.protocolIncludes = [ ]
         self.using = [ ]
+        self.unions = [ ]
         self.protocol = None
 
     def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
     def addProtocolInclude(self, pInc): self.protocolIncludes.append(pInc)
+    def addUnionDecl(self, union): self.unions.append(union)
     def addUsingStmt(self, using): self.using.append(using)
 
     def setProtocol(self, protocol): self.protocol = protocol
 
 class CxxInclude(Node):
     def __init__(self, loc, cxxFile):
         Node.__init__(self, loc)
         self.file = cxxFile
@@ -212,43 +234,43 @@ class OUT:
             'rpc': 'RpcAnswer' },
     OUT : { 'async': 'AsyncSend',
             'sync': 'SyncSend',
             'rpc': 'RpcCall' }
     # inout doesn't make sense here
 }
 
 
-class Protocol(Node):
-    def __init__(self, loc):
+class Namespace(Node):
+    def __init__(self, loc, namespace):
         Node.__init__(self, loc)
-        self.name = None
-        self.namespaces = [ ]
+        self.namespace = namespace
+
+class Protocol(NamespacedNode):
+    def __init__(self, loc):
+        NamespacedNode.__init__(self, loc)
         self.sendSemantics = ASYNC
         self.managesStmts = [ ]
         self.messageDecls = [ ]
         self.transitionStmts = [ ]
         self.startStates = [ ]
 
-    def addOuterNamespace(self, namespace):
-        self.namespaces.insert(0, namespace)
-
     def addManagesStmts(self, managesStmts):
         self.managesStmts += managesStmts
 
     def addMessageDecls(self, messageDecls):
         self.messageDecls += messageDecls
 
     def addTransitionStmts(self, transStmts):
         self.transitionStmts += transStmts
 
-class Namespace(Node):
-    def __init__(self, loc, namespace):
-        Node.__init__(self, loc)
-        self.namespace = namespace
+class UnionDecl(NamespacedNode):
+    def __init__(self, loc, name, components):
+        NamespacedNode.__init__(self, loc, name)
+        self.components = components
 
 class ManagerStmt(Node):
     def __init__(self, loc, managerName):
         Node.__init__(self, loc)
         self.name = managerName
 
 class ManagesStmt(Node):
     def __init__(self, loc, managedName):
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -55,19 +55,22 @@ class Visitor:
         self.visitBlock(ns)
 
     def visitType(self, type):
         pass
 
     def visitTypeEnum(self, enum):
         pass
 
+    def visitTypeUnion(self, union):
+        for t, name in union.components:
+            t.accept(self)
+
     def visitTypedef(self, tdef):
         tdef.fromtype.accept(self)
-        tdef.totype.accept(self)
 
     def visitForwardDecl(self, fd):
         pass
 
     def visitDecl(self, decl):
         decl.type.accept(self)
 
     def visitClass(self, cls):
@@ -115,16 +118,21 @@ class Visitor:
 
     def visitExprPrefixUnop(self, e):
         e.expr.accept(self)
 
     def visitExprBinary(self, e):
         e.left.accept(self)
         e.right.accept(self)
 
+    def visitExprConditional(self, c):
+        c.cond.accept(self)
+        c.ife.accept(self)
+        c.elsee.accept(self)
+
     def visitExprAddrOf(self, eao):
         self.visitExprPrefixUnop(eao)
 
     def visitExprDeref(self, ed):
         self.visitExprPrefixUnop(ed)
 
     def visitExprCast(self, ec):
         ec.expr.accept(self)
@@ -170,16 +178,19 @@ class Visitor:
         si.ifb.accept(self)
         if si.elseb is not None:
             si.elseb.accept(self)
 
     def visitStmtSwitch(self, ss):
         ss.expr.accept(self)
         self.visitBlock(ss)
 
+    def visitStmtBreak(self, sb):
+        pass
+
     def visitStmtExpr(self, se):
         se.expr.accept(self)
 
     def visitStmtReturn(self, sr):
         if sr.expr is not None:
             sr.expr.accept(self)
 
 ##------------------------------
@@ -234,47 +245,84 @@ class Block(Node):
     def addstmts(self, stmts):
         for stmt in stmts:
             self.addstmt(stmt)
 
 ##------------------------------
 # type and decl thingies
 class Namespace(Block):
     def __init__(self, name):
+        assert isinstance(name, str)
+
         Block.__init__(self)
         self.name = name
 
 class Type(Node):
-    def __init__(self, name, const=0, ptr=0, ref=0, actor=0):
+    def __init__(self, name, const=0,
+                 ptr=0, ptrconst=0, ptrptr=0, ptrconstptr=0,
+                 ref=0,
+                 actor=0):
+        """
+To avoid getting fancy with recursive types, we limit the kinds
+of pointer types that can be be constructed.
+
+  ptr            => T*
+  ptrconst       => T* const
+  ptrptr         => T**
+  ptrconstptr    => T* const*
+
+Any type, naked or pointer, can be const (const T) or ref (T&).
+
+The "actor" flag is used internally when we need to know if the C++
+type actually represents an IPDL actor type.
+"""
         Node.__init__(self)
         self.name = name
         self.const = const
         self.ptr = ptr
+        self.ptrconst = ptrconst
+        self.ptrptr = ptrptr
+        self.ptrconstptr = ptrconstptr
         self.ref = ref
         self.actor = actor
         # XXX could get serious here with recursive types, but shouldn't 
         # need that for this codegen
     def __deepcopy__(self, memo):
-        return Type(self.name, self.const, self.ptr, self.ref, self.actor)
+        return Type(self.name,
+                    const=self.const,
+                    ptr=self.ptr, ptrconst=self.ptrconst,
+                    ptrptr=self.ptrptr, ptrconstptr=self.ptrconstptr,
+                    ref=self.ref, actor=self.actor)
 
 class TypeEnum(Node):
     def __init__(self, name=None):
         '''name can be None'''
         Node.__init__(self)
         self.name = name
         self.idnums = [ ]    # pairs of ('Foo', [num]) or ('Foo', None)
 
     def addId(self, id, num=None):
         self.idnums.append((id, num))
 
+class TypeUnion(Node):
+    def __init__(self, name=None):
+        Node.__init__(self)
+        self.name = name
+        self.components = [ ]           # pairs of (Type, name)
+
+    def addComponent(self, type, name):
+        self.components.append((type, name))
+
 class Typedef(Node):
-    def __init__(self, fromtype, totype):
+    def __init__(self, fromtype, totypename):
+        assert isinstance(totypename, str)
+        
         Node.__init__(self)
         self.fromtype = fromtype
-        self.totype = totype
+        self.totypename = totypename
 
 class ForwardDecl(Node):
     def __init__(self, pqname, cls=0, struct=0):
         assert (not cls and struct) or (cls and not struct)
 
         self.pqname = pqname
         self.cls = cls
         self.struct = struct
@@ -287,77 +335,88 @@ class Decl(Node):
         self.name = name
     def __deepcopy__(self, memo):
         return Decl(copy.deepcopy(self.type, memo), self.name)
 
 ##------------------------------
 # class stuff
 class Class(Block):
     def __init__(self, name, inherits=[ ],
-                 interface=False, abstract=False, final=False):
+                 interface=0, abstract=0, final=0,
+                 specializes=None, struct=0):
         assert not (interface and abstract)
         assert not (abstract and final)
         assert not (interface and final)
+        assert not (inherits and specializes)
 
         Block.__init__(self)
         self.name = name
-        self.inherits = inherits
-        self.interface = interface
-        self.abstract = abstract
-        self.final = final
+        self.inherits = inherits        # [ Type ]
+        self.interface = interface      # bool
+        self.abstract = abstract        # bool
+        self.final = final              # bool
+        self.specializes = specializes  # Type or None
+        self.struct = struct            # bool
 
 class Inherit(Node):
     def __init__(self, name, viz='public'):
         Node.__init__(self)
         self.name = name
         self.viz = viz
 
 class FriendClassDecl(Node):
     def __init__(self, friend):
         Node.__init__(self)
         self.friend = friend
 
 class MethodDecl(Node):
     def __init__(self, name, params=[ ], ret=Type('void'),
-                 virtual=False, const=False, pure=False, static=False):
+                 virtual=0, const=0, pure=0, static=0, typeop=0):
         assert not (virtual and static)
-        assert not pure or virtual # pure => virtual
+        assert not pure or virtual      # pure => virtual
+        assert not (static and typeop)
+
+        if typeop:
+            ret = None
 
         Node.__init__(self)
         self.name = name
-        self.params = params
-        self.ret = ret
-        self.virtual = virtual
-        self.const = const
-        self.pure = pure
-        self.static = static
+        self.params = params            # [ Param ]
+        self.ret = ret                  # Type or None
+        self.virtual = virtual          # bool
+        self.const = const              # bool
+        self.pure = pure                # bool
+        self.static = static            # bool
+        self.typeop = typeop            # bool
+        
     def __deepcopy__(self, memo):
         return MethodDecl(self.name,
                           copy.deepcopy(self.params, memo),
                           copy.deepcopy(self.ret, memo),
                           self.virtual,
                           self.const,
                           self.pure)
 
 class MethodDefn(Block):
     def __init__(self, decl):
         Block.__init__(self)
         self.decl = decl
 
 class ConstructorDecl(MethodDecl):
-    def __init__(self, name, params=[ ]):
+    def __init__(self, name, params=[ ], explicit=0):
         MethodDecl.__init__(self, name, params=params, ret=None)
+        self.explicit = explicit
 
 class ConstructorDefn(MethodDefn):
     def __init__(self, decl, memberinits=[ ]):
         MethodDefn.__init__(self, decl)
         self.memberinits = memberinits
 
 class DestructorDecl(MethodDecl):
-    def __init__(self, name, virtual=False):
+    def __init__(self, name, virtual=0):
         MethodDecl.__init__(self, name, params=[ ], ret=None,
                             virtual=virtual)
 class DestructorDefn(MethodDefn):
     def __init__(self, decl):  MethodDefn.__init__(self, decl)
 
 ##------------------------------
 # expressions
 class ExprLiteral(Node):
@@ -409,20 +468,28 @@ class ExprCast(Node):
         self.dynamic = dynamic
         self.static = static
         self.reinterpret = reinterpret
         self.const = const
         self.C = C
 
 class ExprBinary(Node):
     def __init__(self, left, op, right):
+        Node.__init__(self)
         self.left = left
         self.op = op
         self.right = right
 
+class ExprConditional(Node):
+    def __init__(self, cond, ife, elsee):
+        Node.__init__(self)
+        self.cond = cond
+        self.ife = ife
+        self.elsee = elsee
+ 
 class ExprSelect(Node):
     def __init__(self, obj, op, field):
         Node.__init__(self)
         self.obj = obj
         self.op = op
         self.field = field
 
 class ExprAssn(Node):
@@ -435,18 +502,25 @@ class ExprAssn(Node):
 class ExprCall(Node):
     def __init__(self, func, args=[ ]):
         Node.__init__(self)
         self.func = func
         self.args = args
 
 class ExprNew(ExprCall):
     # XXX taking some poetic license ...
-    def __init__(self, type, args=[ ]):
-        ExprCall.__init__(self, ExprVar(type.name), args)
+    def __init__(self, type, args=[ ], newargs=None):
+        assert not (type.const or type.ref)
+        
+        ctorname = type.name
+        if type.ptr:  ctorname += '*'
+        elif type.ptrptr:  ctorname += '**'
+        
+        ExprCall.__init__(self, ExprVar(ctorname), args)
+        self.newargs = newargs
 
 class ExprDelete(Node):
     def __init__(self, obj):
         Node.__init__(self)
         self.obj = obj
 
 class ExprMemberInit(ExprCall):
     def __init__(self, member, args=[ ]):
@@ -462,16 +536,19 @@ class StmtDecl(Node):
     def __init__(self, decl):
         Node.__init__(self)
         self.decl = decl
 
 class Label(Node):
     def __init__(self, name):
         Node.__init__(self)
         self.name = name
+Label.PUBLIC = Label('public')
+Label.PROTECTED = Label('protected')
+Label.PRIVATE = Label('private')
 
 class CaseLabel(Node):
     def __init__(self, name):
         Node.__init__(self)
         self.name = name
 
 class DefaultLabel(Node):
     def __init__(self):
@@ -496,16 +573,20 @@ class StmtSwitch(Block):
         self.nr_cases = 0
 
     def addcase(self, case, block):
         '''NOTE: |case| is not checked for uniqueness'''
         self.addstmt(case)
         self.addstmt(block)
         self.nr_cases += 1
 
+class StmtBreak(Node):
+    def __init__(self):
+        Node.__init__(self)
+
 class StmtExpr(Node):
     def __init__(self, expr):
         Node.__init__(self)
         self.expr = expr
 
 class StmtReturn(Node):
     def __init__(self, expr=None):
         Node.__init__(self)
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -50,20 +50,27 @@ class CxxCodeGen(CodePrinter, Visitor):
 
     def visitNamespace(self, ns):
         self.println('namespace '+ ns.name +' {')
         self.visitBlock(ns)
         self.println('} // namespace '+ ns.name)
 
     def visitType(self, t):
         ts = ''
+
         if t.const:  ts += 'const '
         ts += t.name
-        if t.ptr:  ts += '*'
+
+        if t.ptr:            ts += '*'
+        elif t.ptrconst:     ts += '* const'
+        elif t.ptrptr:       ts += '**'
+        elif t.ptrconstptr:  ts += '* const*'
+
         if t.ref:  ts += '&'
+
         self.write(ts)
 
     def visitTypeEnum(self, te):
         self.write('enum')
         if te.name:
             self.write(' '+ te.name)
         self.println(' {')
 
@@ -75,46 +82,71 @@ class CxxCodeGen(CodePrinter, Visitor):
                 self.write(' = '+ str(num))
             if i != (nids-1):
                 self.write(',')
             self.println()
         self.dedent()
         self.printdent('}')
             
 
+    def visitTypeUnion(self, u):
+        self.write('union')
+        if u.name:
+            self.write(' '+ u.name)
+        self.println(' {')
+
+        self.indent()
+        for t, name in u.components:
+            self.printdent()
+            t.accept(self)
+            self.println(' '+ name +';')
+        self.dedent()
+
+        self.printdent('}')
+
+
     def visitTypedef(self, td):
         self.printdent('typedef ')
         td.fromtype.accept(self)
-        self.write(' ')
-        td.totype.accept(self)
-        self.println(';')
+        self.println(' '+ td.totypename +';')
 
     def visitForwardDecl(self, fd):
         if fd.cls:      self.printdent('class ')
         elif fd.struct: self.printdent('struct ')
         self.write(str(fd.pqname))
         self.println(';')
 
     def visitDecl(self, d):
         d.type.accept(self)
         if d.name:
             self.write(' '+ d.name)
 
     def visitClass(self, c):
-        self.printdent('class')
+        if c.specializes is not None:
+            self.printdentln('template<>')
+        
+        if c.struct:
+            self.printdent('struct')
+        else:
+            self.printdent('class')
         if c.interface:
             # FIXME/cjones: turn this "on" when we get the analysis
             self.write(' /*NS_INTERFACE_CLASS*/')
         if c.abstract:
             # FIXME/cjones: turn this "on" when we get the analysis
             self.write(' /*NS_ABSTRACT_CLASS*/')
         if c.final:
             self.write(' NS_FINAL_CLASS')
         self.write(' '+ c.name)
 
+        if c.specializes is not None:
+            self.write(' <')
+            c.specializes.accept(self)
+            self.write('>')
+
         ninh = len(c.inherits)
         if 0 < ninh:
             self.println(' :')
             self.indent()
             for i, inherit in enumerate(c.inherits):
                 self.printdent()
                 inherit.accept(self)
                 if i != (ninh - 1):
@@ -131,47 +163,51 @@ class CxxCodeGen(CodePrinter, Visitor):
         self.printdentln('};')
 
     def visitInherit(self, inh):
         self.write(inh.viz +' '+ inh.name)
 
     def visitFriendClassDecl(self, fcd):
         self.printdentln('friend class '+ fcd.friend +';')
 
+
     def visitMethodDecl(self, md):
         assert not (md.static and md.virtual)
+
         if md.static:
             self.write('static ')
         if md.virtual:
             self.write('virtual ')
         if md.ret:
             md.ret.accept(self)
             self.write(' ')
         self.write(md.name +'(')
         self.writeDeclList(md.params)
         self.write(')')
         if md.const:
             self.write(' const')
         if md.pure:
             self.write(' = 0')
 
+
     def visitMethodDefn(self, md):
         self.printdent()
         md.decl.accept(self)
         self.println()
 
         self.printdentln('{')
         self.indent()
         self.visitBlock(md)
         self.dedent()
         self.printdentln('}')
 
 
     def visitConstructorDecl(self, cd):
-        # FIXME/cjones: explicit when possible
+        if cd.explicit:
+            self.write('explicit ')
         self.visitMethodDecl(cd)
 
     def visitConstructorDefn(self, cd):
         self.printdent()
         cd.decl.accept(self)
         if len(cd.memberinits):
             self.println(' :')
             self.indent()
@@ -239,16 +275,25 @@ class CxxCodeGen(CodePrinter, Visitor):
 
     def visitExprBinary(self, e):
         self.write('(')
         e.left.accept(self)
         self.write(') '+ e.op +' (')
         e.right.accept(self)
         self.write(')')
 
+    def visitExprConditional(self, c):
+        self.write('(')
+        c.cond.accept(self)
+        self.write(' ? ')
+        c.ife.accept(self)
+        self.write(' : ')
+        c.elsee.accept(self)
+        self.write(')')
+
     def visitExprSelect(self, es):
         es.obj.accept(self)
         self.write(es.op + es.field)
 
     def visitExprAssn(self, ea):
         ea.lhs.accept(self)
         self.write(' '+ ea.op +' ')
         ea.rhs.accept(self)
@@ -256,16 +301,20 @@ class CxxCodeGen(CodePrinter, Visitor):
     def visitExprCall(self, ec):
         ec.func.accept(self)
         self.write('(')
         self.writeExprList(ec.args)
         self.write(')')
 
     def visitExprNew(self, en):
         self.write('new ')
+        if en.newargs is not None:
+            self.write('(')
+            self.writeExprList(en.newargs)
+            self.write(') ')
         self.visitExprCall(en)
 
     def visitExprDelete(self, ed):
         self.write('delete ')
         ed.accept(self)
 
 
     def visitStmtBlock(self, b):
@@ -312,16 +361,19 @@ class CxxCodeGen(CodePrinter, Visitor):
         self.printdent('switch (')
         sw.expr.accept(self)
         self.println(') {')
         self.indent()
         self.visitBlock(sw)
         self.dedent()
         self.printdentln('}')
 
+    def visitStmtBreak(self, sb):
+        self.printdentln('break;')
+
 
     def visitStmtDecl(self, sd):
         self.printdent()
         sd.decl.accept(self)
         self.println(';')
 
 
     def visitStmtExpr(self, se):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -49,27 +49,27 @@ def _protocolHeaderFilename(p, pname):
 
 def _protocolHeaderName(pname):
     return pname
 
 def _actorName(pname, side):
     """|pname| is the protocol name. |side| is 'Parent' or 'Child'."""
     return pname + side
 
-def _makeForwardDecl(p, side):
-    clsname = _actorName(p.decl.type.qname.baseid, side)
+def _makeForwardDecl(ptype, side):
+    clsname = _actorName(ptype.qname.baseid, side)
     
     fd = cxx.ForwardDecl(clsname, cls=1)
-    if 0 == len(p.namespaces):
+    if 0 == len(ptype.qname.quals):
         return fd
 
-    outerns = cxx.Namespace(p.namespaces[0].namespace)
+    outerns = cxx.Namespace(ptype.qname.quals[0])
     innerns = outerns
-    for ns in p.namespaces[1:]:
-        tmpns = cxx.Namespace(ns.namespace)
+    for ns in ptype.qname.quals[1:]:
+        tmpns = cxx.Namespace(ns)
         innerns.addstmt(tmpns)
         innerns = tmpns
 
     innerns.addstmt(fd)
     return outerns
 
 class _struct: pass
 
@@ -107,17 +107,17 @@ class GenerateProtocolHeader(Visitor):
 
     def lower(self, tu, protocol, outcxxfile):
         self.pname = protocol
         self.file = outcxxfile
         tu.accept(self)
 
 
     def typedef(self, t, name):
-        self.typedefs.append(cxx.Typedef(cxx.Type(t), cxx.Type(name)))
+        self.typedefs.append(cxx.Typedef(cxx.Type(t), name))
 
     def injectTypedefs(self, scope):
         for tdef in self.typedefs:
             scope.addstmt(tdef)
 
     def visitTranslationUnit(self, tu):
         f = self.file
 
@@ -145,16 +145,566 @@ class GenerateProtocolHeader(Visitor):
     def visitProtocolInclude(self, inc):
         p = inc.tu.protocol
         self.file.addthing(
             cxx.CppDirective(
                 'include',
                 '"'+ _protocolHeaderFilename(p, _protocolHeaderName(p.name)) +'"'))
 
 
+    def visitUnionDecl(self, ud):
+        # This Union class basically consists of a type (enum) and a a
+        # union for storage, both private.  The union can contain POD
+        # and non-POD types.  Each type needs a copy ctor, assignment
+        # operator, and destructor.
+        #
+        # Rather than templating this class and only providing
+        # specializations for the types we support, which is slightly
+        # "unsafe", we explicitly implement non-templated methods
+        # for each supported type.
+        #
+        # The one complication that arises is that C++, for aracne
+        # reasons, does not allow the placement destructor of a
+        # builtin type, like int, to be directly invoked.  So we need
+        # to hack around this by internally typedef'ing all
+        # constituent types.  Sigh.
+        #
+        # So, for each type, this "Union" class needs:
+        # (private)
+        #  - entry in the type enum
+        #  - entry in the storage union
+        #  - [type]ptr() method to get a type* from the underlying union
+        #  - same as above to get a const type*
+        #  - typedef to hack around placement delete limitations
+        # (public)
+        #  - placement delete case for dtor
+        #  - copy ctor
+        #  - case in generic copy ctor
+        #  - operator= impl
+        #  - case in generic operator=
+        #  - operator [type&]
+        #  - operator [const type&] const
+        #  - [type&] get_[type]()
+        #  - [const type&] get_[type]() const
+        #
+        # We collect all this first, in a loop, then put it back
+        # together
+        #
+        # Actor types are a bit tricky because they have different
+        # interfaces in different processes.  We fix this by stuffing
+        # interfaces for both processes into this implementation.
+        # In the parent process, we only expect the parent to get
+        # ActorParent*'s, and similarly in the child.  If this
+        # invariant can be broken, we have bigger problems than
+        # instantiating illegal IPDL discriminated unions.
+        cls = cxx.Class(ud.name, final=1)
+        clstyperef = cxx.Type(ud.name, ref=1)
+        typetype = cxx.Type('Type')
+        valuetype = cxx.Type('Value')
+        mtypevar = cxx.ExprVar('mType')
+        mvaluevar = cxx.ExprVar('mValue')
+        assertsanityvar = cxx.ExprVar('AssertSanity')
+
+        # useful exprs/stmts that can be reused (read-only)
+        valuevar = cxx.ExprVar('aValue')
+        rhsvar = cxx.ExprVar('aRhs')
+        returnthis = cxx.StmtReturn(cxx.ExprDeref(cxx.ExprVar('this')))
+        returnfalse = cxx.StmtReturn(cxx.ExprVar('false'))
+        returntrue = cxx.StmtReturn(cxx.ExprVar('true'))
+        hardabort = cxx.StmtExpr(cxx.ExprCall(
+            cxx.ExprVar('NS_RUNTIMEABORT'),
+            [ cxx.ExprLiteral.String("catastrophically unexpected type") ]))
+            
+        enumvs = [ ]
+        union = cxx.TypeUnion('Value')
+        union.hasActor = False
+        ptrmeths = [ ]
+        constptrmeths = [ ]
+        typedefs = [ ]
+        dtorswitch = cxx.StmtSwitch(mtypevar)
+        copyctors = [ ]
+        copyswitch = cxx.StmtSwitch(mtypevar)
+        opeqs = [ ]
+        opeqswitch = cxx.StmtSwitch(mtypevar)
+        optypes = [ ]
+        opconsttypes = [ ]
+        gettypes = [ ]
+        getconsttypes = [ ]
+        writeswitch = cxx.StmtSwitch(
+            cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('aParam'), '.', 'type')))
+        readswitch = cxx.StmtSwitch(cxx.ExprVar('type'))
+
+        # "templates" copied and altered multiple times below
+        basiccopydecl = cxx.ConstructorDecl(
+            ud.name,
+            params=[ cxx.Decl(cxx.Type('!!DUMMY!!', const=1, ref=1),
+                              'aValue') ],
+            explicit=1)
+        basicopeqdecl = cxx.MethodDecl(
+            'operator=',
+            params=[ cxx.Decl(cxx.Type('!!DUMMY!!', const=1, ref=1),
+                              'aRhs') ],
+            ret=cxx.Type(ud.name, ref=1))
+
+        # we generate code for both Parent and Child actors
+        cxxtypes = [ ]
+        forwarddecls = [ ]              # forward decls of actor types
+        usingTypedefs = [ ]             # typedefs of namespaced |using| types
+        for t in ud.decl.type.components:
+            if t.isIPDL() and t.isActor():
+                p = cxx.Type(t.name() +'Parent', ptr=1)
+                p._ipdl = t
+                p._side = 'Parent'
+                c = cxx.Type(t.name() +'Child', ptr=1)
+                c._ipdl = t
+                c._side = 'Child'
+                cxxtypes.append(p)
+                cxxtypes.append(c)
+                # we shouldn't have *any* actor's definition in scope
+                # from within a protocol header.  so forward-declare
+                # actor types
+                forwarddecls.append(_makeForwardDecl(t.protocol, 'Parent'))
+                forwarddecls.append(_makeForwardDecl(t.protocol, 'Child'))
+            else:
+                ct = cxx.Type(t.name())
+                ct._ipdl = None
+                cxxtypes.append(ct)
+                if t.name() != t.fullname():
+                    usingTypedefs.append(cxx.Typedef(cxx.Type(t.fullname()),
+                                                     t.name()))
+
+        # special, opaque handle used to pack away actor types when unions
+        # are sent across the wire
+        t = cxx.Type('ActorHandle');  t._ipdl = None
+        cxxtypes.append(t)
+
+        for cxxt in cxxtypes:
+            cxxtptr = deepcopy(cxxt)
+            if cxxt.ptr:
+                cxxtptr.ptr = 0
+                cxxtptr.ptrptr = 1
+            else:
+                cxxtptr.ptr = 1
+            cxxtconstptr = deepcopy(cxxtptr)
+            cxxtconstptr.const = 1
+            if cxxt._ipdl is not None:
+                cxxtconstptr.ptrptr = 0
+                cxxtconstptr.ptrconstptr = 1
+            cxxtdefname = cxxt.name +'__tdef'
+            typename = cxxt.name
+            consttypename = cxxt.name
+            if cxxt.ptr:
+                typename += '*'
+                consttypename += '* const'
+
+            # actor types are pointers, don't use copy semantics
+            copyintype = deepcopy(cxxt)
+            if cxxt._ipdl is None:
+                # not an actor type
+                copyintype.const = 1
+                copyintype.ref = 1
+
+            enumv = 'T'+ cxxt.name
+            enumvar = cxx.ExprVar(enumv)
+            unionname = 'V'+ cxxt.name
+            caselabel = cxx.CaseLabel(enumv)
+            fullcaselabel = cxx.CaseLabel(ud.decl.fullname +'::'+ enumv)
+            gettypen = 'get_'+ cxxt.name
+            gettypevar = cxx.ExprVar(gettypen)
+            returngettype = cxx.StmtReturn(cxx.ExprCall(cxx.ExprVar(gettypen)))
+
+            enumvs.append(enumv)
+            cxxt._tag = enumv
+            
+            union.addComponent(cxxt, unionname)
+
+            ptrmethdecl = cxx.MethodDecl(name='ptr'+ cxxt.name,
+                                         ret=cxxtptr)
+            callptrmeth = cxx.ExprCall(cxx.ExprVar('ptr'+ cxxt.name))
+            ptrmeth = cxx.MethodDefn(ptrmethdecl)
+            ptrmeth.addstmt(cxx.StmtReturn(
+                cxx.ExprCast(
+                    cxx.ExprAddrOf(cxx.ExprSelect(mvaluevar, '.', unionname)),
+                    cxxtptr,
+                    reinterpret=1)))
+            ptrmeths.append(ptrmeth)
+            constptrmethdecl = cxx.MethodDecl(name='constptr'+ cxxt.name,
+                                              ret=cxxtconstptr,
+                                              const=1)
+            callconstptrmeth = cxx.ExprCall(cxx.ExprVar('constptr'+ cxxt.name))
+            constptrmeth = cxx.MethodDefn(constptrmethdecl)
+            constptrmeth.addstmt(cxx.StmtReturn(
+                cxx.ExprCast(
+                    cxx.ExprAddrOf(cxx.ExprSelect(mvaluevar, '.', unionname)),
+                    cxxtconstptr,
+                    reinterpret=1)))
+            constptrmeths.append(constptrmeth)
+
+            typedefs.append(cxx.Typedef(cxxt, cxxtdefname))
+
+            dtorswitch.addstmt(caselabel)
+            dtorswitch.addstmt(cxx.StmtExpr(
+                cxx.ExprCall(
+                    cxx.ExprSelect(callptrmeth,
+                                   '->', '~'+ cxxtdefname))))
+            dtorswitch.addstmt(cxx.StmtBreak())
+
+            copyctordecl = deepcopy(basiccopydecl)
+            copyctordecl.params[0].type = copyintype
+            copyctor = cxx.ConstructorDefn(
+                copyctordecl,
+                memberinits=[ cxx.ExprMemberInit(mtypevar,
+                                                 args=[ enumvar ]) ])
+            copyctor.addstmt(cxx.StmtExpr(
+                cxx.ExprNew(cxxt,
+                            args=[ valuevar ],
+                            newargs=[ callptrmeth ])))
+            copyctors.append(copyctor)
+
+            copyswitch.addstmt(caselabel)
+            copyinval = cxx.ExprCall(cxx.ExprSelect(valuevar, '.', gettypen))
+            if cxxt._ipdl is not None:
+                # we don't use copy semantics for actor pointers, so
+                # we have to const_cast away the access to
+                # |(constOther&).getActorPtr()| to get a naked pointer
+                # again
+                copyinval = cxx.ExprCast(copyinval, cxxt, const=1)
+            copyswitch.addstmt(cxx.StmtExpr(
+                cxx.ExprNew(cxxt,
+                            args=[ copyinval ],
+                            newargs=[ callptrmeth ])))
+            copyswitch.addstmt(cxx.StmtBreak())
+
+            opeqdecl = deepcopy(basicopeqdecl)
+            opeqdecl.params[0].type = copyintype
+            opeq = cxx.MethodDefn(opeqdecl)
+            opeq.addstmt(cxx.StmtExpr(cxx.ExprAssn(mtypevar, enumvar)))
+            opeq.addstmt(cxx.StmtExpr(
+                cxx.ExprAssn(cxx.ExprDeref(callptrmeth),
+                             rhsvar)))
+            opeq.addstmt(returnthis)
+            opeqs.append(opeq)
+
+            opeqswitch.addstmt(caselabel)
+            opeqswitch.addstmt(cxx.StmtExpr(
+                cxx.ExprAssn(cxx.ExprDeref(callptrmeth),
+                             cxx.ExprCast(rhsvar, clstyperef, const=1))))
+            opeqswitch.addstmt(cxx.StmtBreak())
+
+            optype = cxx.MethodDefn(
+                cxx.MethodDecl('operator '+ typename +'&', typeop=1))
+            optype.addstmt(returngettype)
+            optypes.append(optype)
+
+            opconsttype = cxx.MethodDefn(
+                cxx.MethodDecl('operator const '+ consttypename +'&',
+                               const=1, typeop=1))
+            opconsttype.addstmt(returngettype)
+            opconsttypes.append(opconsttype)
+
+            callassertsanetype = cxx.StmtExpr(
+                cxx.ExprCall(assertsanityvar,
+                             args=[ enumvar ]))
+
+            rettype = deepcopy(cxxt)
+            rettype.ref = 1
+            gettype = cxx.MethodDefn(cxx.MethodDecl(gettypen,
+                                                    ret=rettype))
+            gettype.addstmt(callassertsanetype)
+            gettype.addstmt(cxx.StmtReturn(cxx.ExprDeref(callptrmeth)))
+            gettypes.append(gettype)
+
+            constrettype = deepcopy(rettype)
+            constrettype.const = 1
+            if cxxt._ipdl is not None:
+                constrettype.ptr = 0
+                constrettype.ptrconst = 1
+            getconsttype = cxx.MethodDefn(cxx.MethodDecl(gettypen,
+                                                         ret=constrettype,
+                                                         const=1))
+            getconsttype.addstmt(callassertsanetype)
+            getconsttype.addstmt(cxx.StmtReturn(
+                cxx.ExprDeref(callconstptrmeth)))
+            getconsttypes.append(getconsttype)
+
+            # only create case if this isn't an IPDL actor type.
+            # there are specially serialized in the Send* method
+            if cxxt._ipdl is None:
+                writeswitch.addstmt(fullcaselabel)
+                case = cxx.StmtBlock()
+                case.addstmt(cxx.StmtExpr(
+                    cxx.ExprCall(
+                        cxx.ExprVar('WriteParam'),
+                        args=[ cxx.ExprVar('aMsg'),
+                               cxx.ExprCall(
+                                   cxx.ExprSelect(cxx.ExprVar('aParam'),
+                                                  '.', gettypen)) ])))
+                case.addstmt(cxx.StmtReturn())
+                writeswitch.addstmt(case)
+
+                # actor types are similarly special-cased in the
+                # deserializer
+                readswitch.addstmt(fullcaselabel)
+                case = cxx.StmtBlock()
+                case.addstmt(cxx.StmtDecl(cxx.Decl(cxxt, 'val')))
+                valvar = cxx.ExprVar('val')
+                readval = cxx.ExprCall(
+                    cxx.ExprVar('ReadParam'),
+                    args=[ cxx.ExprVar('aMsg'),
+                           cxx.ExprVar('aIter'),
+                           cxx.ExprAddrOf(valvar) ])
+                failif = cxx.StmtIf(cxx.ExprPrefixUnop(readval, '!'))
+                failif.addifstmt(cxx.StmtReturn(cxx.ExprVar('false')))
+                case.addstmt(failif)
+                case.addstmt(cxx.StmtExpr(
+                    cxx.ExprAssn(cxx.ExprDeref(cxx.ExprVar('aResult')),
+                                 valvar)))
+                case.addstmt(returntrue)
+                readswitch.addstmt(case)
+            else:
+                union.hasActor = True
+
+
+        # now put all these decls and defn's together into a class
+        cls.addstmt(cxx.Label.PRIVATE)
+        for td in usingTypedefs:
+            cls.addstmt(td)
+        cls.addstmt(cxx.Typedef(cxx.Type('mozilla::ipc::ActorHandle'),
+                                'ActorHandle'))
+        for tdef in typedefs:
+            cls.addstmt(tdef)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        cls.addstmt(cxx.Label.PUBLIC)
+        tenum = cxx.TypeEnum('Type')
+        for enumv in enumvs:
+            tenum.addId(enumv)
+        tenum.addId('T__First', enumvs[0])
+        tenum.addId('T__Last', enumvs[len(enumvs)-1])
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(tenum, '')))
+        cls.addstmt(cxx.Whitespace.NL)
+
+        cls.addstmt(cxx.Label.PRIVATE)
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(union, '')))
+        for ptrmeth in ptrmeths:
+            cls.addstmt(ptrmeth)
+        for constptrmeth in constptrmeths:
+            cls.addstmt(constptrmeth)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        # sanity checker
+        sanity = cxx.MethodDefn(cxx.MethodDecl(assertsanityvar.name,
+                                               ret=cxx.Type('void'),
+                                               const=1))
+        sanity.addstmt(cxx.StmtExpr(
+            cxx.ExprCall(cxx.ExprVar('NS_ABORT_IF_FALSE'),
+                         [ cxx.ExprBinary(cxx.ExprVar('T__First'),
+                                          '<=',
+                                          mtypevar),
+                           cxx.ExprLiteral.String('invalid type tag') ])))
+        sanity.addstmt(cxx.StmtExpr(
+            cxx.ExprCall(cxx.ExprVar('NS_ABORT_IF_FALSE'),
+                         [ cxx.ExprBinary(mtypevar,
+                                          '<=',
+                                          cxx.ExprVar('T__Last')),
+                           cxx.ExprLiteral.String('invalid type tag') ])))
+        cls.addstmt(sanity)
+        sanity = cxx.MethodDefn(
+            cxx.MethodDecl(assertsanityvar.name,
+                           params=[ cxx.Decl(typetype, 'aType') ],
+                           ret=cxx.Type('void'),
+                           const=1))
+        sanity.addstmt(cxx.StmtExpr(cxx.ExprCall(assertsanityvar)))
+        sanity.addstmt(cxx.StmtExpr(
+            cxx.ExprCall(
+                cxx.ExprVar('NS_ABORT_IF_FALSE'),
+                [ cxx.ExprBinary(mtypevar, '==', cxx.ExprVar('aType')),
+                  cxx.ExprLiteral.String('unexpected type tag') ])))
+        cls.addstmt(sanity)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        cls.addstmt(cxx.Label.PUBLIC)
+        # default ctor
+        cls.addstmt(
+            cxx.ConstructorDefn(
+                cxx.ConstructorDecl(ud.name),
+                [ cxx.ExprMemberInit(mtypevar,
+                                     [ cxx.ExprCast(cxx.ExprLiteral.ZERO,
+                                                    typetype,
+                                                    static=1) ]) ]))
+        for copyctor in copyctors:
+            cls.addstmt(copyctor)
+        # copy ctor(const Union&)
+        copyctordecl = deepcopy(basiccopydecl)
+        copyctordecl.params[0].type.name = ud.name
+        copyctor = cxx.MethodDefn(copyctordecl)
+        copyctor.addstmt(cxx.StmtExpr(cxx.ExprCall(
+            cxx.ExprSelect(valuevar,
+                           '.', assertsanityvar.name))))
+        copyctor.addstmt(cxx.StmtExpr(
+            cxx.ExprAssn(mtypevar,
+                         cxx.ExprSelect(valuevar,
+                                        '.', 'mType'))))
+        copyctor.addstmt(copyswitch)
+        cls.addstmt(copyctor)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        # dtor
+        # we'll hit the default: case if the union var was never assigned
+        dtorswitch.addstmt(cxx.DefaultLabel())
+        dtorswitch.addstmt(cxx.StmtBreak())
+        dtor = cxx.DestructorDefn(cxx.DestructorDecl(ud.name))
+        dtor.addstmt(dtorswitch)
+        cls.addstmt(dtor)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        # operator='s
+        for opeq in opeqs:
+            cls.addstmt(opeq)
+        opeqdecl = deepcopy(basicopeqdecl)
+        opeqdecl.params[0].type.name = ud.name
+        opeq = cxx.MethodDefn(opeqdecl)
+        opeq.addstmt(cxx.StmtExpr(
+            cxx.ExprCall(cxx.ExprSelect(rhsvar, '.', assertsanityvar.name))))
+        opeq.addstmt(cxx.StmtExpr(
+            cxx.ExprAssn(mtypevar, cxx.ExprSelect(rhsvar, '.', 'mType'))))
+        opeq.addstmt(opeqswitch)
+        opeq.addstmt(cxx.StmtReturn(cxx.ExprDeref(cxx.ExprVar('this'))))
+        cls.addstmt(opeq)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        # |operator [type]|'s and get_[type]s
+        for optype in optypes:
+            cls.addstmt(optype)
+        for opconsttype in opconsttypes:
+            cls.addstmt(opconsttype)
+        for gettype in gettypes:
+            cls.addstmt(gettype)
+        for getconsttype in getconsttypes:
+            cls.addstmt(getconsttype)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        gettype = cxx.MethodDefn(cxx.MethodDecl('type', ret=typetype, const=1))
+        gettype.addstmt(cxx.StmtExpr(cxx.ExprCall(assertsanityvar)))
+        gettype.addstmt(cxx.StmtReturn(mtypevar))
+        cls.addstmt(gettype)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        cls.addstmt(cxx.Label.PRIVATE)
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(typetype, 'mType')))
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(valuetype, 'mValue')))
+
+        # the "ParamTraits" specialization:
+        # serialization, deserialization, and logging
+        uniontype = cxx.Type(ud.decl.type.fullname())
+        pickle = cxx.Class('ParamTraits', specializes=uniontype, struct=1)
+        pickle.addstmt(cxx.Label.PRIVATE)
+        for td in usingTypedefs:
+            pickle.addstmt(td)
+        pickle.addstmt(cxx.Typedef(cxx.Type('mozilla::ipc::ActorHandle'),
+                                   'ActorHandle'))
+        pickle.addstmt(cxx.Label.PUBLIC)
+        pickle.addstmt(cxx.Typedef(uniontype, 'paramType'))
+        pickle.addstmt(cxx.Whitespace.NL)
+
+        serialize = cxx.MethodDefn(cxx.MethodDecl(
+            'Write',
+            params=[ cxx.Decl(cxx.Type('Message', ptr=1), 'aMsg'),
+                     cxx.Decl(cxx.Type('paramType', const=1, ref=1), 'aParam') ],
+            ret=cxx.Type('void'),
+            static=1))
+        serialize.addstmt(cxx.StmtExpr(
+            cxx.ExprCall(
+                cxx.ExprVar('WriteParam'),
+                args=[
+                    cxx.ExprVar('aMsg'),
+                    cxx.ExprCast(
+                        cxx.ExprCall(
+                            cxx.ExprSelect(cxx.ExprVar('aParam'),
+                                           '.', 'type')),
+                        cxx.Type('int'),
+                        static=1) ])))
+        # default: blow up
+        writeswitch.addstmt(cxx.DefaultLabel())
+        writeswitch.addstmt(hardabort)
+        serialize.addstmt(writeswitch)
+        pickle.addstmt(serialize)
+
+        deserialize = cxx.MethodDefn(
+            cxx.MethodDecl(
+                'Read',
+                params=[ cxx.Decl(cxx.Type('Message', const=1, ptr=1),
+                                  'aMsg'),
+                         cxx.Decl(cxx.Type('void', ptrptr=1),
+                                  'aIter'),
+                         cxx.Decl(cxx.Type('paramType', ptr=1),
+                                  'aResult') ],
+                ret=cxx.Type('bool'),
+                static=1))
+        deserialize.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('int'), 'type')))
+        readtype = cxx.ExprCall(
+            cxx.ExprVar('ReadParam'),
+            args=[ cxx.ExprVar('aMsg'),
+                   cxx.ExprVar('aIter'),
+                   cxx.ExprAddrOf(cxx.ExprVar('type')) ])
+        failif = cxx.StmtIf(cxx.ExprPrefixUnop(readtype, '!'))
+        failif.ifb.addstmt(returnfalse)
+        deserialize.addstmt(failif)
+        # default: blow up
+        readswitch.addstmt(cxx.DefaultLabel())
+        readswitch.addstmt(returnfalse)
+        deserialize.addstmt(readswitch)
+        pickle.addstmt(deserialize)
+
+        logger = cxx.MethodDefn(
+            cxx.MethodDecl(
+                'Log',
+                params=[ cxx.Decl(cxx.Type('paramType', const=1, ref=1),
+                                  'aParam'),
+                         cxx.Decl(cxx.Type('std::wstring', ptr=1),
+                                  'aLog') ],
+                static=1))
+        # FIXME: real implementation
+        logger.addstmt(cxx.StmtExpr(cxx.ExprCall(
+            cxx.ExprSelect(cxx.ExprVar('aLog'), '->', 'append'),
+            args=[ cxx.ExprLiteral.WString('('+ ud.name +')') ])))
+        pickle.addstmt(logger)
+
+        # preserves information about which component types
+        # are actors, and what the type tags of those actors
+        # are.  we use this info for the special-cased code
+        # in Send/Recv methods
+        ud.decl.type._cxxunion = union
+
+        # stick generated classes into the right namespaces
+        if 0 == len(ud.namespaces):
+            nscls = cls
+        else:
+            nscls = cxx.Namespace(ud.namespaces[0].namespace)
+            innerns = nscls
+            for ns in ud.namespaces[1:]:
+                tmp = cxx.Namespace(ns.namespace)
+                innerns.addstmt(tmp)
+                innerns = tmp
+            innerns.addstmt(cls)
+
+        nspickle = cxx.Namespace('IPC')
+        nspickle.addstmt(pickle)
+
+        self.file.addthing(cxx.Whitespace.NL)
+        for fd in forwarddecls:
+            self.file.addthing(fd)
+            self.file.addthing(cxx.Whitespace.NL)
+        self.file.addthing(cxx.Whitespace.NL)
+        self.file.addthing(nscls)
+        self.file.addthing(cxx.Whitespace.NL)
+        self.file.addthing(nspickle)
+        self.file.addthing(cxx.Whitespace.NL)
+
+
     def visitUsingStmt(self, using):
         if using.decl.fullname is not None:
             self.typedef(using.decl.fullname, using.decl.shortname)
 
 
     def visitProtocol(self, p):
         self.file.addthing(cxx.Whitespace.NL)
 
@@ -221,23 +771,32 @@ class GenerateProtocolHeader(Visitor):
         ns.addstmt(cxx.Whitespace.NL)
         ns.addstmt(cxx.Whitespace.NL)
 
 
     def visitMessageDecl(self, md):
         # where we squirrel away some common information
         md._cxx = _struct()
 
+        def canonicalType(t):
+            cxxt = cxx.Type(t.name())
+            if t.isIPDL() and t.isUnionType():
+                cxxt._union = t._cxxunion
+            else:
+                cxxt._union = None
+
+            if t.isIPDL() and t.isActor():
+                cxxt.actor = 1
+                cxxt.ptr = 1
+
+            return cxxt
+
         def makeCxxDecl(decl):
-            cxxd = cxx.Decl(cxx.Type(decl.type.name()),
+            return cxx.Decl(canonicalType(decl.type),
                             decl.progname)
-            if decl.type.isIPDL() and decl.type.isActor():
-                cxxd.type.actor = 1
-                cxxd.type.ptr = 1
-            return cxxd
 
         md._cxx.params = [ makeCxxDecl(d) for d in md.inParams ]
         md._cxx.returns = [ makeCxxDecl(d) for d in md.outParams ]
 
         # generate C++ interface to message sending/handling
         method = cxx.MethodDecl(
             name=md.decl.progname,
             params=[ ],
@@ -268,21 +827,21 @@ class GenerateProtocolHeader(Visitor):
 
     def visitTransitionStmt(self, ts):
         ts.state.decl._cxxname = 'State_%s__ID'% (ts.state.decl.progname)
 
 
 def generateMsgClass(md, clsname, params, typedefInjector):
         cls = cxx.Class(name=clsname,
                         inherits=[ cxx.Inherit('IPC::Message') ])
-        cls.addstmt(cxx.Label('private'))
+        cls.addstmt(cxx.Label.PRIVATE)
         typedefInjector(cls)
         cls.addstmt(cxx.Whitespace.NL)
 
-        cls.addstmt(cxx.Label('public'))
+        cls.addstmt(cxx.Label.PUBLIC)
 
         idenum = cxx.TypeEnum()
         idenum.addId('ID', clsname +'__ID')
         cls.addstmt(cxx.StmtDecl(cxx.Decl(idenum, '')))
 
         constparams = deepcopy(params)
         writestmts = [ ]
         for cparam in constparams:
@@ -308,17 +867,16 @@ def generateMsgClass(md, clsname, params
 
         ctor = cxx.ConstructorDefn(ctordecl, [ superinit ])
         for cparam in constparams:
             ctor.addstmt(
                 cxx.StmtExpr(cxx.ExprCall(cxx.ExprVar('IPC::WriteParam'),
                                           [ cxx.ExprVar('this'),
                                             cxx.ExprVar(cparam.name) ])))
         cls.addstmt(ctor)
-
         cls.addstmt(cxx.Whitespace.NL)
 
         # make the message deserializer
         outparams = deepcopy(params)
         for oparam in outparams:
             if oparam.type.actor:
                 # this is an actor. it comes across the wire as a Handle
                 oparam.type = cxx.Type('mozilla::ipc::ActorHandle')
@@ -549,31 +1107,32 @@ class GenerateProtocolActorHeader(Visito
     def visitProtocolInclude(self, pi):
         p = pi.tu.protocol
 
         if self.protocol.decl.type.isManagerOf(p.decl.type):
             header = _protocolHeaderFilename(
                 p, _actorName(p.name, self.myside))
             self.file.addthing(cxx.CppDirective('include', '"'+ header +'"'))
         else:
-            self.file.addthing(_makeForwardDecl(p, self.myside))
+            self.file.addthing(_makeForwardDecl(p.decl.type, self.myside))
             
         if p.decl.fullname is not None:
             self.typedefs.append(cxx.Typedef(
                 cxx.Type(_actorName(p.decl.fullname, self.myside)),
-                cxx.Type(_actorName(p.decl.shortname, self.myside))))
+                _actorName(p.decl.shortname, self.myside)))
 
     def visitProtocol(self, p):
         p._cxx = _struct()
 
         self.file.addthing(cxx.CppDirective('ifdef', 'DEBUG'))
         self.file.addthing(cxx.CppDirective('include', '"prenv.h"'))
         self.file.addthing(cxx.CppDirective('endif', '// DEBUG'))
 
-        if p.decl.type.isManager():
+        # FIXME: all actors impl Iface for now
+        if p.decl.type.isManager() or 1:
             self.file.addthing(cxx.CppDirective('include', '"base/id_map.h"'))
 
         # bug 510041: we need to claim to implement the listener
         # interface for the top-level protocol's channel type, rather
         # for the channel type that this protocol wants
         sendsemantics = p.decl.type.toplevel().sendSemantics
         channel = _channelTable[sendsemantics]
         channellistener = _listenerTable[sendsemantics]
@@ -620,17 +1179,17 @@ class GenerateProtocolActorHeader(Visito
         cls = cxx.Class(self.clsname, inherits=inherits, abstract=True)
 
         if p.decl.type.isManaged():
             cls.addstmt(cxx.FriendClassDecl(
                 _actorName(p.decl.type.manager.fullname(),
                            self.myside)))
             cls.addstmt(cxx.Whitespace.NL)
 
-        cls.addstmt(cxx.Label('protected'))
+        cls.addstmt(cxx.Label.PROTECTED)
         for typedef in self.typedefs:
             cls.addstmt(typedef)
         cls.addstmt(cxx.Whitespace.NL)
 
         # message handlers the subclass has to impl
         for md in p.messageDecls:
             if md.decl.type.isCtor() or md.decl.type.isDtor():
                 objtype = cxx.Type(
@@ -669,26 +1228,26 @@ class GenerateProtocolActorHeader(Visito
                     dummyimpl = cxx.MethodDefn(meth)
                     dummyimpl.addstmt(cxx.StmtReturn(cxx.ExprVar('NS_OK')))
                     cls.addstmt(dummyimpl)
                 else:
                     meth.pure = True
                     cls.addstmt(cxx.StmtDecl(meth))
         cls.addstmt(cxx.Whitespace.NL)
 
-        cls.addstmt(cxx.Label('private'))
+        cls.addstmt(cxx.Label.PRIVATE)
         cls.addstmt(cxx.Typedef(cxx.Type('IPC::Message'),
-                                cxx.Type('Message')))
+                                'Message'))
         cls.addstmt(cxx.Typedef(cxx.Type(channelname),
-                                cxx.Type('Channel')))
+                                'Channel'))
         cls.addstmt(cxx.Typedef(cxx.Type(channellistener),
-                                cxx.Type('ChannelListener')))
+                                'ChannelListener'))
         cls.addstmt(cxx.Whitespace.NL)
         
-        cls.addstmt(cxx.Label('public'))
+        cls.addstmt(cxx.Label.PUBLIC)
         ctor = cxx.ConstructorDefn(cxx.ConstructorDecl(self.clsname))
         if p.decl.type.isToplevel():
             ctor.memberinits = [
                 cxx.ExprMemberInit(
                     cxx.ExprVar('mChannel'),
                     [ cxx.ExprCall(
                             cxx.ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
                             [ cxx.ExprVar('this') ]) ]) ]
@@ -830,18 +1389,22 @@ class GenerateProtocolActorHeader(Visito
                                   [ cxx.ExprVar('msg'), cxx.ExprVar('reply') ])
                 if self.rpcswitch.nr_cases > 1:
                     rpchandler.addstmt(self.rpcswitch)
                 else:
                     rpchandler.addstmt(reterror)
                 cls.addstmt(rpchandler)
                 cls.addstmt(cxx.Whitespace.NL)
 
-        # implement IProtocolManager interface
-        if p.decl.type.isManager():
+        # implement IProtocolManager interface.
+        #
+        # FIXME: only manager protocols and non-manager protocols with
+        # union types need Lookup().  we'll give it to all for the
+        # time being (simpler)
+        if 1 or p.decl.type.isManager():
             register = cxx.MethodDefn(
                 cxx.MethodDecl(
                     'Register',
                     [ cxx.Decl(cxx.Type('ChannelListener', ptr=1), 'aRouted') ],
                     ret=cxx.Type('int32'),
                     virtual=1))
             registerid = cxx.MethodDefn(
                 cxx.MethodDecl(
@@ -880,54 +1443,55 @@ class GenerateProtocolActorHeader(Visito
                 registerid.addstmt(cxx.StmtExpr(
                         cxx.ExprCall(
                             cxx.ExprSelect(cxx.ExprVar('mActorMap'),
                                            '.', 'AddWithID'),
                             [ cxx.ExprVar('aRouted'), idvar ])))
                 registerid.addstmt(cxx.StmtReturn(idvar))
 
                 lookup.addstmt(cxx.StmtReturn(
-                        cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mActorMap'),
-                                                    '.', 'Lookup'),
-                                     [ cxx.ExprVar('aId') ])))
+                    cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mActorMap'),
+                                                '.', 'Lookup'),
+                                 [ cxx.ExprVar('aId') ])))
                 unregister.addstmt(cxx.StmtReturn(
                         cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mActorMap'),
                                                     '.', 'Remove'),
                                      [ cxx.ExprVar('aId') ])))
             else:
                 register.addstmt(cxx.StmtReturn(
                         cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mManager'),
                                                     '->', 'Register'),
                                      [ cxx.ExprVar('aRouted') ])))
                 registerid.addstmt(cxx.StmtReturn(
                         cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mManager'),
                                                     '->', 'RegisterID'),
                                      [ cxx.ExprVar('aRouted'), 
                                        cxx.ExprVar('aId') ])))
                 lookup.addstmt(cxx.StmtReturn(
-                        cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mManager'),
-                                                    '->', 'Lookup'),
-                                     [ cxx.ExprVar('aId') ])))
+                    cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mManager'),
+                                                '->', 'Lookup'),
+                                 [ cxx.ExprVar('aId') ])))
                 unregister.addstmt(cxx.StmtReturn(
                         cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('mManager'),
                                                     '->', 'Unregister'),
                                      [ cxx.ExprVar('aId') ])))
             cls.addstmt(register)
             cls.addstmt(registerid)
             cls.addstmt(lookup)
             cls.addstmt(unregister)
             cls.addstmt(cxx.Whitespace.NL)
 
         # private members and methods
-        cls.addstmt(cxx.Label('private'))
+        cls.addstmt(cxx.Label.PRIVATE)
         channeltype = cxx.Type('Channel')
         if p.decl.type.isManaged():
             channeltype.ptr = True # subprotocols inherit this
         cls.addstmt(cxx.StmtDecl(cxx.Decl(channeltype, 'mChannel')))
-        if p.decl.type.isToplevel() and p.decl.type.isManager():
+        # FIXME: all protocols get manager Iface
+        if p.decl.type.isToplevel() and (1 or p.decl.type.isManager()):
             cls.addstmt(cxx.StmtDecl(cxx.Decl(
                         cxx.Type('IDMap<ChannelListener>'), 'mActorMap')))
             cls.addstmt(cxx.StmtDecl(cxx.Decl(
                         cxx.Type('int'), p._cxx.routeidvar.name)))
         elif p.decl.type.isManaged():
             cls.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('int'), 'mId')))
             cls.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('int'), 'mPeerId')))
             cls.addstmt(cxx.StmtDecl(cxx.Decl(
@@ -1074,46 +1638,143 @@ class GenerateProtocolActorHeader(Visito
                 impl.addstmt(cxx.StmtDecl(cxx.Decl(
                             cxx.Type('mozilla::ipc::ActorHandle'), '__ah')))
                 ahvar = cxx.ExprVar('__ah')
                 impl.addstmt(cxx.StmtExpr(
                         cxx.ExprAssn(cxx.ExprSelect(ahvar, '.', 'mId'),
                                      objid)))
                 impl.addstmt(cxx.Whitespace.NL)
 
-            # if this message has explicit actor params, we need to
-            # convert them to Handle's before sending
+            hasreply = md.decl.type.hasReply()
+            if hasreply:
+                impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('Message'),
+                                                   '__reply')))
+                replyvar = cxx.ExprVar('__reply')
+
+            impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(md._cxx.nsid, ptr=1),
+                                               '__msg')))
+            msgvar = cxx.ExprVar('__msg')
+
+            # special case for explicit actor params: they need to be
+            # converted to Handle's before sending
             for param in mdecl.params:
                 if not param.type.actor:
                     continue
                 pavar = cxx.ExprVar(param.name)
                 pahvar = cxx.ExprVar(param.name +'__ah')
                 impl.addstmt(cxx.StmtDecl(cxx.Decl(
                     cxx.Type('mozilla::ipc::ActorHandle'), pahvar.name)))
                 impl.addstmts(_actorToActorHandle(actor=pavar,
                                                   handle=pahvar,
                                                   failcode=valueerrcode))
             impl.addstmt(cxx.Whitespace.NL)
 
-            hasreply = md.decl.type.hasReply()
-            if hasreply:
-                impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('Message'),
-                                                   '__reply')))
-                replyvar = cxx.ExprVar('__reply')
+            # special case for IPDL union types: if a union
+            # contains an actor type, we secretly convert
+            # it into a serializable representation
+            uahvars = [ ]
+            wasrepackedvars = [ ]
+            ruid = 0
+            switches = [ ]
+            for p in md._cxx.params:
+                if (p.type._union is None
+                    or not p.type._union.hasActor):
+                    uahvars.append(None)
+                    wasrepackedvars.append(None)
+                    continue
+
+                pvar = cxx.ExprVar(p.name)
+                u = p.type._union
+
+                uahdecl = cxx.StmtDecl(cxx.Decl(
+                    cxx.Type('mozilla::ipc::ActorHandle'),
+                    '__uah'+ str(ruid)))
+                uahvar = cxx.ExprVar('__uah'+ str(ruid))
+                uahvars.append( (uahdecl, uahvar) )
+
+                wrvar = cxx.ExprVar('__wr_'+ str(ruid))
+                wrdecl = cxx.StmtDecl(cxx.Decl(cxx.Type('bool'), wrvar.name))
+                wasrepackedvars.append( (wrdecl, wrvar) )
+                ruid += 1
+                # if the union currently stores an actor, unpack the
+                # pointer and repack it as an ActorHandle
+                switch = cxx.StmtSwitch(cxx.ExprCall(
+                    cxx.ExprSelect(pvar, '.', 'type')))
+                for t, n in u.components:
+                    if (t._ipdl is None
+                        or self.myside is not t._side):
+                        continue
+                    switch.addstmt(cxx.CaseLabel(p.type.name +'::'+ t._tag))
+                    repack = cxx.StmtBlock()
+                    repack.addstmt(cxx.StmtDecl(cxx.Decl(t, '__ua')))
 
-            impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(md._cxx.nsid, ptr=1),
-                                               '__msg')))
-            msgvar = cxx.ExprVar('__msg')
+                    uavar = cxx.ExprVar('__ua')
+                    # take the actor out of the union and convert it
+                    # to an ActorHandle
+                    #
+                    # need to const_cast the union because it comes in
+                    # as a const&.  we promise to be gentle
+                    utype = deepcopy(p.type)
+                    utype.ref = 1
+                    repack.addstmt(cxx.StmtExpr(cxx.ExprAssn(
+                        uavar,
+                        cxx.ExprCast(pvar, utype, const=1))))
+
+                    repack.addstmts(_actorToActorHandle(actor=uavar,
+                                                       handle=uahvar,
+                                                       failcode=valueerrcode))
+
+                    # record that this param indeed did have an actor param
+                    repack.addstmt(cxx.StmtExpr(cxx.ExprAssn(
+                        wrvar, cxx.ExprVar('true'))))
+                    repack.addstmt(cxx.StmtBreak())
+                    switch.addstmt(repack)
+
+                # if not actor type, don't care
+                switch.addstmt(cxx.DefaultLabel())
+                switch.addstmt(cxx.StmtBreak())
+                switches.append(switch)
+
+            for udv in uahvars:
+                if udv is None: continue
+                uahdecl, _ = udv
+                impl.addstmt(uahdecl)
+            for wrdv in wasrepackedvars:
+                if wrdv is None: continue
+                wrdecl, wrvar = wrdv
+                impl.addstmt(wrdecl)
+                impl.addstmt(cxx.StmtExpr(
+                    cxx.ExprAssn(wrvar, cxx.ExprVar('false'))))
+            impl.addstmt(cxx.Whitespace.NL)
+                
+            for switch in switches:
+                impl.addstmt(switch)
+            impl.addstmt(cxx.Whitespace.NL)
 
             msgctorargs = [ ]
-            for param in md._cxx.params:
-                aname = param.name
-                if param.type.actor:
-                    aname += '__ah'
-                msgctorargs.append(cxx.ExprVar(aname))
+            for i, param in enumerate(md._cxx.params):
+                pvar = cxx.ExprVar(param.name)
+                wrdv = wasrepackedvars[i]
+
+                if wrdv is None:
+                    # not a param with union type
+                    if param.type.actor:
+                        # explicit actor param.  need to send the Handle
+                        pvar.name += '__ah'
+                    msgctorargs.append(pvar)
+                else:
+                    wrvar = wrdv[1]
+                    uahvar = uahvars[i][1]
+                    # if the union-type variable had an actor value and
+                    # was unpacked, send the re-packed ActorHandle.
+                    # we rely on implicit conversion from ActorHandle
+                    # to the union type
+                    msgctorargs.append(
+                        cxx.ExprConditional(wrvar, uahvar, pvar))
+                
             if md.decl.type.hasImplicitActorParam():
                 msgctorargs.append(ahvar)
                 
             msgctor = cxx.ExprNew(cxx.Type(md._cxx.nsid), msgctorargs)
                 
             if self.p.decl.type.isManaged():
                 route = cxx.ExprVar('mId')
             else:
@@ -1184,16 +1845,64 @@ class GenerateProtocolActorHeader(Visito
                     unpackargs.append(cxx.ExprAddrOf(ahvar))
 
                 unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsreplyid +'::Read'),
                                       unpackargs)
                 errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
                 errhandle.ifb.addstmt(cxx.StmtReturn(valueerrcode))
                 impl.addstmt(errhandle)
 
+                # see if we need to extract an actor out of a union
+                # and re-convert it into an actor pointer
+                for r in md._cxx.returns:
+                    if (r.type._union is None
+                        or not r.type._union.hasActor):
+                        continue
+                    u = r.type._union
+                    for t, n in u.components:
+                        if (t._ipdl is None
+                            or self.myside is not t._side):
+                            continue
+                        rvar = cxx.ExprVar(r.name)
+                        # FIXME/cjones: we're not special-casing these
+                        # enough. currently limited to one actor per
+                        # union
+                        ifhandle = cxx.StmtIf(
+                            cxx.ExprBinary(
+                                cxx.ExprVar(r.type.name +'::TActorHandle'),
+                                '==',
+                                cxx.ExprCall(cxx.ExprSelect(rvar,
+                                                            '.', 'type'))))
+                        ifhandle.addifstmt(cxx.StmtDecl(
+                            cxx.Decl(cxx.Type('mozilla::ipc::ActorHandle'),
+                                              '__uah')))
+                        uahvar = cxx.ExprVar('__uah')
+                        ifhandle.addifstmt(cxx.StmtExpr(cxx.ExprAssn(uahvar,
+                                                                     rvar)))
+                        # look up and verify the actor handle we got
+                        ifhandle.addifstmt(cxx.StmtDecl(cxx.Decl(t, '__ua')))
+                        uavar = cxx.ExprVar('__ua')
+                        actorid = cxx.ExprSelect(uahvar, '.', 'mId')
+                        cast = cxx.ExprCast(
+                            cxx.ExprCall(cxx.ExprVar('Lookup'), [ actorid ]),
+                            t,
+                            static=1)
+                        ifhandle.addifstmt(cxx.StmtExpr(
+                            cxx.ExprAssn(uavar, cast)))
+                        failif = cxx.StmtIf(cxx.ExprPrefixUnop(uavar, '!'))
+                        failif.addifstmt(cxx.StmtReturn(
+                            cxx.ExprVar('MsgValueError')))
+                        ifhandle.addifstmt(failif)
+
+                        # finally, slam the actor back into the union
+                        ifhandle.addifstmt(cxx.StmtExpr(cxx.ExprAssn(
+                            rvar, uavar)))
+                        
+                        impl.addstmt(ifhandle)
+
                 # log the reply, maybe
                 injectLogger(impl,
                              cxx.ExprCast(cxx.ExprAddrOf(replyvar),
                                           cxx.Type(md._cxx.nsreplyid, ptr=1),
                                           static=1),
                              'got reply ')
 
             if md.decl.type.isCtor():
@@ -1293,17 +2002,66 @@ class GenerateProtocolActorHeader(Visito
             block.addstmt(errhandle)
 
             injectLogger(block, 
                          cxx.ExprCast(cxx.ExprAddrOf(msgvar),
                                       cxx.Type(md._cxx.nsid, ptr=1, const=1),
                                       static=1),
                          pfx +' ')
 
-            # match up Actor*s specified by Handles
+            # FIXME/cjones: bad copypasta juju
+            #
+            # if a union stored an actor, it was re-packed as an
+            # actor handle.  if so, we need to extract that handle,
+            # convert it back to an actor, then stuff it back into
+            # the union
+            for p in md._cxx.params:
+                if (p.type._union is None
+                    or not p.type._union.hasActor):
+                    continue
+                u = p.type._union
+                for t, n in u.components:
+                    if (t._ipdl is None
+                        or self.myside is not t._side):
+                        continue
+                    pvar = cxx.ExprVar(p.name)
+                    # FIXME/cjones: we're not special-casing these
+                    # enough. currently limited to one actor per
+                    # union
+                    ifhandle = cxx.StmtIf(
+                        cxx.ExprBinary(
+                            cxx.ExprVar(p.type.name +'::TActorHandle'),
+                            '==',
+                            cxx.ExprCall(cxx.ExprSelect(pvar,
+                                                        '.', 'type'))))
+                    ifhandle.addifstmt(cxx.StmtDecl(
+                        cxx.Decl(cxx.Type('mozilla::ipc::ActorHandle'),
+                                 '__uah')))
+                    uahvar = cxx.ExprVar('__uah')
+                    ifhandle.addifstmt(cxx.StmtExpr(cxx.ExprAssn(uahvar,
+                                                                 pvar)))
+                    # look up and verify the actor handle we got
+                    ifhandle.addifstmt(cxx.StmtDecl(cxx.Decl(t, '__ua')))
+                    uavar = cxx.ExprVar('__ua')
+
+                    ifhandle.ifb.addstmts(
+                        _actorHandleToActor(
+                            handle=uahvar,
+                            actor=uavar,
+                            actortype=t,
+                            failcode=cxx.ExprVar('MsgValueError')))
+
+                    # finally, slam the actor back into the union
+                    ifhandle.addifstmt(cxx.StmtExpr(
+                        cxx.ExprAssn(pvar, uavar)))
+                        
+                    block.addstmt(ifhandle)
+
+            # convert explicit actor params, iced as ActorHandles,
+            # back into actors
             for param in md._cxx.params:
                 if not param.type.actor: continue
 
                 pavar = cxx.ExprVar(param.name)
                 pahvar = cxx.ExprVar(pavar.name +'__ah')
                 actortype = deepcopy(param.type)
                 actortype.name = _actorName(actortype.name, self.myside)
                 block.addstmts(
@@ -1394,19 +2152,100 @@ class GenerateProtocolActorHeader(Visito
                 block.addstmt(cxx.StmtExpr(
                         cxx.ExprAssn(routevar, cxx.ExprLiteral.ZERO)))
 
             if md.decl.type.hasReply():
                 if not md.decl.type.hasReply():
                     block.addstmt(cxx.StmtDecl(
                             cxx.Decl(cxx.Type('Message', ptr=1), 'reply')))
                 replyvar = cxx.ExprVar('reply')
-                replymsgctor = cxx.ExprNew(
-                    cxx.Type(md._cxx.nsreplyid),
-                    [ cxx.ExprVar(r.name) for r in md._cxx.returns ])
+
+                # FIXME/cjones: yet more copy-n-paste
+                #
+                # special case for IPDL union types: if this union
+                # contains an actor type, we secretly convert
+                # it into a serializable representation
+                uahvars = [ ]
+                wasrepackedvars = [ ]
+                ruid = 0
+                switches = [ ]
+                for r in md._cxx.returns:
+                    if (r.type._union is None
+                        or not r.type._union.hasActor):
+                        uahvars.append(None)
+                        wasrepackedvars.append(None)
+                        continue
+
+                    rvar = cxx.ExprVar(r.name)
+                    u = r.type._union
+
+                    uahdecl = cxx.StmtDecl(cxx.Decl(
+                        cxx.Type('mozilla::ipc::ActorHandle'),
+                        '__uah'+ str(ruid)))
+                    uahvar = cxx.ExprVar('__uah'+ str(ruid))
+                    uahvars.append( (uahdecl, uahvar) )
+
+                    wrvar = cxx.ExprVar('__wr_'+ str(ruid))
+                    wrdecl = cxx.StmtDecl(cxx.Decl(cxx.Type('bool'),
+                                                   wrvar.name))
+                    wasrepackedvars.append( (wrdecl, wrvar) )
+                    ruid += 1
+                    # if the union currently stores an actor, unpack the
+                    # pointer and repack it as an ActorHandle
+                    switch = cxx.StmtSwitch(cxx.ExprCall(
+                        cxx.ExprSelect(rvar, '.', 'type')))
+                    for t, n in u.components:
+                        if (t._ipdl is None
+                            or self.myside is not t._side):
+                            continue
+                        switch.addstmt(cxx.Label(t._tag))
+                        repack = cxx.StmtBlock()
+                        repack.addstmt(cxx.StmtDecl(cxx.Decl(t, '__ua')))
+                        uavar = cxx.ExprVar('__ua')
+                        repack.addstmt(cxx.StmtExpr(cxx.ExprAssn(uavar, rvar)))
+                        repack.addstmt(cxx.StmtExpr(
+                            cxx.ExprAssn(cxx.ExprSelect(uahvar, '.', 'mId'),
+                                         cxx.ExprSelect(uavar, '.', 'mId'))))
+                        repack.addstmt(cxx.StmtExpr(cxx.ExprAssn(rvar,
+                                                                 uahvar)))
+                        repack.addstmt(cxx.StmtExpr(cxx.ExprAssn(
+                            wrvar, cxx.ExprVar('true'))))
+                        repack.addstmt(cxx.StmtBreak())
+                        switch.addstmt(repack)
+                    # if it's not an actor, don't care
+                    switch.addstmt(cxx.DefaultLabel())
+                    switches.append(switch)
+
+                for udv in uahvars:
+                    if udv is None: continue
+                    uahdecl, _ = udv
+                    block.addstmt(uahdecl)
+                for wrdv in wasrepackedvars:
+                    if wrdv is None: continue
+                    wrdecl, wrvar = wrdv
+                    block.addstmt(wrdecl)
+                    block.addstmt(cxx.StmtExpr(
+                        cxx.ExprAssn(wrvar, cxx.ExprVar('false'))))
+                for switch in switches:
+                    block.addstmt(switch)
+
+                ctorparams = [ ]
+                for i, r in enumerate(md._cxx.returns):
+                    rvar = cxx.ExprVar(r.name)
+                    wrdv = wasrepackedvars[i]
+                    if wrdv is None:
+                        ctorparams.append(rvar)
+                    else:
+                        wrvar = wrdv[1]
+                        uahvar = uahvars[i][1]
+                        ctorparams.append(
+                            cxx.ExprConditional(wrvar, uahvar, rvar))
+
+                replymsgctor = cxx.ExprNew(cxx.Type(md._cxx.nsreplyid),
+                                           args=ctorparams)
                 if md.decl.type.hasImplicitActorParam():
                     replymsgctor.args.append(ahvar)
                 block.addstmt(cxx.StmtExpr(cxx.ExprAssn(replyvar,
                                                         replymsgctor)))
                 block.addstmt(cxx.StmtExpr(cxx.ExprCall(
                             cxx.ExprSelect(replyvar, '->', 'set_reply'),
                             [ ])))
 
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -163,16 +163,17 @@ reserved = set((
         'returns',
         'rpc',
         'send',
         'share',
         'start',
         'state',
         'sync',
         'transfer',
+        'union',
         'using'))
 tokens = [
     'COLONCOLON', 'ID', 'STRING'
 ] + [ r.upper() for r in reserved ]
 
 t_COLONCOLON = '::'
 
 literals = '(){}[]<>;:,~'
@@ -202,25 +203,38 @@ def t_STRING(t):
 
 def t_error(t):
     _error(Loc(Parser.current.filename, t.lineno),
            'lexically invalid characters `%s', t.value)
 
 ##-----------------------------------------------------------------------------
 
 def p_TranslationUnit(p):
-    """TranslationUnit : Preamble NamespacedProtocolDefn"""
+    """TranslationUnit : Preamble NamespacedStuff"""
     tu = Parser.current.tu
     for stmt in p[1]:
-        if isinstance(stmt, CxxInclude):  tu.addCxxInclude(stmt)
-        elif isinstance(stmt, ProtocolInclude): tu.addProtocolInclude(stmt)
-        elif isinstance(stmt, UsingStmt): tu.addUsingStmt(stmt)
+        if isinstance(stmt, CxxInclude):
+            tu.addCxxInclude(stmt)
+        elif isinstance(stmt, ProtocolInclude):
+            tu.addProtocolInclude(stmt)
+        elif isinstance(stmt, UsingStmt):
+            tu.addUsingStmt(stmt)
         else:
             assert 0
-    tu.protocol = p[2]
+
+    for thing in p[2]:
+        if isinstance(thing, UnionDecl):
+            tu.addUnionDecl(thing)
+        elif isinstance(thing, Protocol):
+            if tu.protocol is not None:
+                _error(thing.loc, "only one protocol definition per file")
+            tu.protocol = thing
+        else:
+            assert(0)
+
     p[0] = tu
 
 ##--------------------
 ## Preamble
 def p_Preamble(p):
     """Preamble : Preamble PreambleStmt ';'
                 |"""
     if 1 == len(p):
@@ -253,26 +267,49 @@ def p_ProtocolIncludeStmt(p):
     inc.tu = Parser().parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout)
     p[0] = inc
 
 def p_UsingStmt(p):
     """UsingStmt : USING CxxType"""
     p[0] = UsingStmt(locFromTok(p, 1), p[2])
 
 ##--------------------
-## Protocol definition
-def p_NamespacedProtocolDefn(p):
-    """NamespacedProtocolDefn : NAMESPACE ID '{' NamespacedProtocolDefn '}'
-                              | ProtocolDefn"""
+## Namespaced stuff
+def p_NamespacedStuff(p):
+    """NamespacedStuff : NamespacedStuff NamespaceThing
+                       | NamespaceThing"""
     if 2 == len(p):
         p[0] = p[1]
     else:
-        protocol = p[4]
-        protocol.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
-        p[0] = protocol
+        p[1].extend(p[2])
+        p[0] = p[1]
+
+def p_NamespaceThing(p):
+    """NamespaceThing : NAMESPACE ID '{' NamespacedStuff '}'
+                      | UnionDecl
+                      | ProtocolDefn"""
+    if 2 == len(p):
+        p[0] = [ p[1] ]
+    else:
+        for thing in p[4]:
+            thing.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
+        p[0] = p[4]
+    
+def p_UnionDecl(p):
+    """UnionDecl : UNION ID '{' ComponentTypes  '}' ';'"""
+    p[0] = UnionDecl(locFromTok(p, 1), p[2], p[4])
+
+def p_ComponentTypes(p):
+    """ComponentTypes : ComponentTypes Type ';'
+                      | Type ';'"""
+    if 3 == len(p):
+        p[0] = [ p[1] ]
+    else:
+        p[1].append(p[2])
+        p[0] = p[1]
 
 def p_ProtocolDefn(p):
     """ProtocolDefn : OptionalSendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts OptionalMessageDecls TransitionStmts '}' ';'"""
     protocol = Protocol(locFromTok(p, 2))
     protocol.name = p[3]
     protocol.sendSemantics = p[1]
     protocol.manager = p[5]
     protocol.addManagesStmts(p[6])
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -113,16 +113,17 @@ class GeneratedCxxType(CxxType):
 ##--------------------
 class IPDLType(Type):
     def isIPDL(self):  return True
     def isVisible(self): return True
     def isState(self): return False
     def isMessage(self): return False
     def isProtocol(self): return False
     def isActor(self): return False
+    def isUnion(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
     def isRpc(self): return self.sendSemantics is RPC
 
     def talksAsync(self): return True
     def talksSync(self): return self.isSync() or self.isRpc()
     def talksRpc(self): return self.isRpc()
@@ -194,26 +195,35 @@ class ProtocolType(IPDLType):
     def isManager(self):
         return len(self.manages) > 0
     def isManaged(self):
         return self.manager is not None
     def isToplevel(self):
         return not self.isManaged()
 
 class ActorType(IPDLType):
-    def __init__(self, protocol, state):
+    def __init__(self, protocol, state=None):
         self.protocol = protocol
         self.state = state
     def isActor(self): return True
 
     def name(self):
         return self.protocol.name()
     def fullname(self):
         return self.protocol.fullname()
 
+class UnionType(IPDLType):
+    def __init__(self, qname, components):
+        self.qname = qname
+        self.components = components
+
+    def isUnionType(self): return True
+    def name(self): return self.qname.baseid
+    def fullname(self): return str(self.qname)
+
 ##--------------------
 _builtinloc = Loc('<builtin>', 0)
 def makeBuiltinUsing(tname):
     quals = tname.split('::')
     base = quals.pop()
     quals = quals[0:]
     return UsingStmt(_builtinloc,
                      TypeSpec(_builtinloc,
@@ -390,18 +400,17 @@ class GatherDecls(TcheckVisitor):
             self.error(p.loc,
                        "expected file defining protocol `%s' to be named `%s'; instead it's named `%s'",
                        p.name, expectedfilename, basefilename)
 
         # FIXME/cjones: it's a little weird and counterintuitive to put
         # both the namespace and non-namespaced name in the global scope.
         # try to figure out something better; maybe a type-neutral |using|
         # that works for C++ and protocol types?
-        qname = QualifiedId(p.loc, p.name,
-                            [ ns.namespace for ns in p.namespaces ])
+        qname = p.qname()
         if 0 == len(qname.quals):
             fullname = None
         else:
             fullname = str(qname)
         p.decl = self.declare(
             loc=p.loc,
             type=ProtocolType(qname, p.sendSemantics),
             shortname=p.name,
@@ -410,28 +419,58 @@ class GatherDecls(TcheckVisitor):
         # make sure we have decls for all dependent protocols
         for pinc in tu.protocolIncludes:
             pinc.accept(self)
 
         # declare imported (and builtin) C++ types
         for using in tu.using:
             using.accept(self)
 
+        # declare unions
+        for union in tu.unions:
+            union.accept(self)
+
         # grab symbols in the protocol itself
         p.accept(self)
 
         tu.type = VOID
 
         self.symtab = savedSymtab
 
 
     def visitProtocolInclude(self, pi):
         pi.tu.accept(self)
         self.symtab.declare(pi.tu.protocol.decl)
 
+    def visitUnionDecl(self, ud):
+        qname = ud.qname()
+        if 0 == len(qname.quals):
+            fullname = None
+        else:
+            fullname = str(qname)
+        components = [ ]
+
+        nactors = 0                     # FIXME
+        
+        for c in ud.components:
+            ctype = self.symtab.lookup(str(c)).type
+            if ctype.isIPDL() and ctype.isProtocol():
+                ctype = ActorType(ctype)
+                nactors += 1
+            components.append(ctype)
+
+        if nactors > 1:
+            self.error(ud.loc, 'sorry, IPDL currently limits you to one actor type per union. file a bug against :cjones')
+       
+        ud.decl = self.declare(
+            loc=ud.loc,
+            type=UnionType(qname, components),
+            shortname=ud.name,
+            fullname=fullname)
+
     def visitUsingStmt(self, using):
         fullname = str(using.type)
         if using.type.basename() == fullname:
             fullname = None
         using.decl = self.declare(
             loc=using.loc,
             type=ImportedCxxType(using.type.spec),
             shortname=using.type.basename(),