From 11c6e75c70493675bc039bf4ddc9d5868fc1d80a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 10 Sep 2009 23:55:03 -0500 Subject: [PATCH] add discriminated union types to IPDL --- dom/plugins/PPluginInstance.ipdl | 29 + dom/plugins/PPluginScriptableObject.ipdl | 1 - dom/plugins/PluginInstanceChild.h | 56 ++ dom/plugins/PluginMessageUtils.h | 34 + dom/plugins/PluginModuleParent.cpp | 9 + ipc/ipdl/ipdl/ast.py | 44 +- ipc/ipdl/ipdl/cxx/ast.py | 125 ++- ipc/ipdl/ipdl/cxx/cgen.py | 64 +- ipc/ipdl/ipdl/lower.py | 945 +++++++++++++++++++++-- ipc/ipdl/ipdl/parser.py | 61 +- ipc/ipdl/ipdl/type.py | 45 +- 11 files changed, 1305 insertions(+), 108 deletions(-) diff --git a/dom/plugins/PPluginInstance.ipdl b/dom/plugins/PPluginInstance.ipdl index 852bec1939bd..96e13c3fec2d 100644 --- a/dom/plugins/PPluginInstance.ipdl +++ b/dom/plugins/PPluginInstance.ipdl @@ -46,9 +46,29 @@ 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; @@ -56,6 +76,15 @@ rpc protocol PPluginInstance manages PPluginScriptableObject; manages PBrowserStream; + + + // FIXME/bent: demo purposes only +child: + Test(Variant v1, Variant v2); + + + + child: /* NPP_NewStream */ rpc PBrowserStream(nsCString url, diff --git a/dom/plugins/PPluginScriptableObject.ipdl b/dom/plugins/PPluginScriptableObject.ipdl index 016740c323e5..f559ee4789cf 100644 --- a/dom/plugins/PPluginScriptableObject.ipdl +++ b/dom/plugins/PPluginScriptableObject.ipdl @@ -57,7 +57,6 @@ rpc protocol PPluginScriptableObject manager PPluginInstance; child: - // NPClass methods rpc Invalidate(); diff --git a/dom/plugins/PluginInstanceChild.h b/dom/plugins/PluginInstanceChild.h index 17036a3242d2..3c4de23a44e5 100644 --- a/dom/plugins/PluginInstanceChild.h +++ b/dom/plugins/PluginInstanceChild.h @@ -65,6 +65,62 @@ class PluginInstanceChild : public PPluginInstanceChild 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); diff --git a/dom/plugins/PluginMessageUtils.h b/dom/plugins/PluginMessageUtils.h index a251c66b6d9a..ec4e6bdc7537 100644 --- a/dom/plugins/PluginMessageUtils.h +++ b/dom/plugins/PluginMessageUtils.h @@ -47,6 +47,14 @@ #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; @@ -364,6 +372,32 @@ struct ParamTraits } }; +template<> +struct ParamTraits +{ + 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 +{ + 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 { diff --git a/dom/plugins/PluginModuleParent.cpp b/dom/plugins/PluginModuleParent.cpp index 804c3a4fa2e6..7bde6e863b0b 100644 --- a/dom/plugins/PluginModuleParent.cpp +++ b/dom/plugins/PluginModuleParent.cpp @@ -188,6 +188,15 @@ PluginModuleParent::NPP_New(NPMIMEType pluginType, 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, diff --git a/ipc/ipdl/ipdl/ast.py b/ipc/ipdl/ipdl/ast.py index 7b3da01ea686..c31ee5939bff 100644 --- a/ipc/ipdl/ipdl/ast.py +++ b/ipc/ipdl/ipdl/ast.py @@ -42,6 +42,8 @@ class Visitor: 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) @@ -54,6 +56,10 @@ class Visitor: # and pass-specific handling pass + def visitUnionDecl(self, union): + for t in union.components: + t.accept(self) + def visitUsingStmt(self, using): pass @@ -133,6 +139,20 @@ class Node: 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) @@ -140,10 +160,12 @@ class TranslationUnit(Node): 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 @@ -217,20 +239,20 @@ _prettyTable = { } -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 @@ -240,10 +262,10 @@ class Protocol(Node): 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): diff --git a/ipc/ipdl/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py index 19a3fd403612..7047d570226d 100644 --- a/ipc/ipdl/ipdl/cxx/ast.py +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -60,9 +60,12 @@ class Visitor: 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 @@ -120,6 +123,11 @@ class Visitor: 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) @@ -175,6 +183,9 @@ class Visitor: ss.expr.accept(self) self.visitBlock(ss) + def visitStmtBreak(self, sb): + pass + def visitStmtExpr(self, se): se.expr.accept(self) @@ -239,21 +250,47 @@ class Block(Node): # 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): @@ -265,11 +302,22 @@ class TypeEnum(Node): 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): @@ -292,17 +340,21 @@ class Decl(Node): # 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'): @@ -317,18 +369,24 @@ class FriendClassDecl(Node): 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), @@ -343,8 +401,9 @@ class MethodDefn(Block): 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=[ ]): @@ -352,7 +411,7 @@ class ConstructorDefn(MethodDefn): 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): @@ -414,10 +473,18 @@ class ExprCast(Node): 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) @@ -440,8 +507,15 @@ class ExprCall(Node): 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): @@ -467,6 +541,9 @@ 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): @@ -501,6 +578,10 @@ class StmtSwitch(Block): 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) diff --git a/ipc/ipdl/ipdl/cxx/cgen.py b/ipc/ipdl/ipdl/cxx/cgen.py index 3bff336a8842..153156a09a1d 100644 --- a/ipc/ipdl/ipdl/cxx/cgen.py +++ b/ipc/ipdl/ipdl/cxx/cgen.py @@ -55,10 +55,17 @@ class CxxCodeGen(CodePrinter, Visitor): 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): @@ -80,12 +87,26 @@ class CxxCodeGen(CodePrinter, Visitor): 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 ') @@ -99,7 +120,13 @@ class CxxCodeGen(CodePrinter, Visitor): 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*/') @@ -110,6 +137,11 @@ class CxxCodeGen(CodePrinter, Visitor): 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(' :') @@ -136,8 +168,10 @@ class CxxCodeGen(CodePrinter, Visitor): 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: @@ -153,6 +187,7 @@ class CxxCodeGen(CodePrinter, Visitor): if md.pure: self.write(' = 0') + def visitMethodDefn(self, md): self.printdent() md.decl.accept(self) @@ -166,7 +201,8 @@ class CxxCodeGen(CodePrinter, Visitor): def visitConstructorDecl(self, cd): - # FIXME/cjones: explicit when possible + if cd.explicit: + self.write('explicit ') self.visitMethodDecl(cd) def visitConstructorDefn(self, cd): @@ -244,6 +280,15 @@ class CxxCodeGen(CodePrinter, Visitor): 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) @@ -261,6 +306,10 @@ class CxxCodeGen(CodePrinter, Visitor): 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): @@ -317,6 +366,9 @@ class CxxCodeGen(CodePrinter, Visitor): self.dedent() self.printdentln('}') + def visitStmtBreak(self, sb): + self.printdentln('break;') + def visitStmtDecl(self, sd): self.printdent() diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index d9e7d200c91f..c6b7912d0e86 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -54,17 +54,17 @@ 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 @@ -112,7 +112,7 @@ class GenerateProtocolHeader(Visitor): 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: @@ -150,6 +150,556 @@ class GenerateProtocolHeader(Visitor): '"'+ _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) @@ -226,13 +776,22 @@ class GenerateProtocolHeader(Visitor): # 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 ] @@ -273,11 +832,11 @@ class GenerateProtocolHeader(Visitor): 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') @@ -313,7 +872,6 @@ def generateMsgClass(md, clsname, params, typedefInjector): [ cxx.ExprVar('this'), cxx.ExprVar(cparam.name) ]))) cls.addstmt(ctor) - cls.addstmt(cxx.Whitespace.NL) # make the message deserializer @@ -554,12 +1112,12 @@ class GenerateProtocolActorHeader(Visitor): 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() @@ -568,7 +1126,8 @@ class GenerateProtocolActorHeader(Visitor): 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 @@ -625,7 +1184,7 @@ class GenerateProtocolActorHeader(Visitor): 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) @@ -674,16 +1233,16 @@ class GenerateProtocolActorHeader(Visitor): 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 = [ @@ -835,8 +1394,12 @@ class GenerateProtocolActorHeader(Visitor): 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', @@ -885,9 +1448,9 @@ class GenerateProtocolActorHeader(Visitor): 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'), @@ -903,9 +1466,9 @@ class GenerateProtocolActorHeader(Visitor): [ 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'), @@ -917,12 +1480,13 @@ class GenerateProtocolActorHeader(Visitor): 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'), 'mActorMap'))) cls.addstmt(cxx.StmtDecl(cxx.Decl( @@ -1079,8 +1643,18 @@ class GenerateProtocolActorHeader(Visitor): 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 @@ -1093,22 +1667,109 @@ class GenerateProtocolActorHeader(Visitor): 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 - impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(md._cxx.nsid, ptr=1), - '__msg'))) - msgvar = cxx.ExprVar('__msg') + 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'))) + + 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) @@ -1189,6 +1850,54 @@ class GenerateProtocolActorHeader(Visitor): 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), @@ -1298,7 +2007,56 @@ class GenerateProtocolActorHeader(Visitor): 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 @@ -1399,9 +2157,90 @@ class GenerateProtocolActorHeader(Visitor): 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, diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py index ae26ac0f25a6..7a12517d921d 100644 --- a/ipc/ipdl/ipdl/parser.py +++ b/ipc/ipdl/ipdl/parser.py @@ -168,6 +168,7 @@ reserved = set(( 'state', 'sync', 'transfer', + 'union', 'using')) tokens = [ 'COLONCOLON', 'ID', 'STRING' @@ -207,15 +208,28 @@ def t_error(t): ##----------------------------------------------------------------------------- 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 ##-------------------- @@ -258,16 +272,39 @@ def p_UsingStmt(p): 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 '}' ';'""" diff --git a/ipc/ipdl/ipdl/type.py b/ipc/ipdl/ipdl/type.py index b3df82d60d7d..d418d37fc2f0 100644 --- a/ipc/ipdl/ipdl/type.py +++ b/ipc/ipdl/ipdl/type.py @@ -118,6 +118,7 @@ class IPDLType(Type): 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 @@ -199,7 +200,7 @@ class ProtocolType(IPDLType): 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 @@ -209,6 +210,15 @@ class ActorType(IPDLType): 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('', 0) def makeBuiltinUsing(tname): @@ -395,8 +405,7 @@ class GatherDecls(TcheckVisitor): # 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: @@ -415,6 +424,10 @@ class GatherDecls(TcheckVisitor): 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) @@ -427,6 +440,32 @@ class GatherDecls(TcheckVisitor): 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: