Bug 613442, part 1: Frontend support for IPDL |opens|. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Fri, 03 Jun 2011 13:33:56 -0500
changeset 70539 50002ea47d7626c56b5a4333913065a15dc95b43
parent 70538 f821b1e1347801ebd544e68077ee6ea15a204b94
child 70540 2d9aaa28ddfa45bdecec3f148159e093409ca222
push id20349
push usercjones@mozilla.com
push dateFri, 03 Jun 2011 18:34:25 +0000
treeherdermozilla-central@e864474d7404 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs613442
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 613442, part 1: Frontend support for IPDL |opens|. r=bent
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -72,16 +72,18 @@ class Visitor:
 
     def visitProtocol(self, p):
         for namespace in p.namespaces:
             namespace.accept(self)
         for spawns in p.spawnsStmts:
             spawns.accept(self)
         for bridges in p.bridgesStmts:
             bridges.accept(self)
+        for opens in p.opensStmts:
+            opens.accept(self)
         for mgr in p.managers:
             mgr.accept(self)
         for managed in p.managesStmts:
             managed.accept(self)
         for msgDecl in p.messageDecls:
             msgDecl.accept(self)
         for transitionStmt in p.transitionStmts:
             transitionStmt.accept(self)
@@ -90,16 +92,19 @@ class Visitor:
         pass
 
     def visitSpawnsStmt(self, spawns):
         pass
 
     def visitBridgesStmt(self, bridges):
         pass
 
+    def visitOpensStmt(self, opens):
+        pass
+
     def visitManager(self, mgr):
         pass
 
     def visitManagesStmt(self, mgs):
         pass
 
     def visitMessageDecl(self, md):
         for inParam in md.inParams:
@@ -264,16 +269,17 @@ class Namespace(Node):
         self.name = namespace
 
 class Protocol(NamespacedNode):
     def __init__(self, loc):
         NamespacedNode.__init__(self, loc)
         self.sendSemantics = ASYNC
         self.spawnsStmts = [ ]
         self.bridgesStmts = [ ]
+        self.opensStmts = [ ]
         self.managers = [ ]
         self.managesStmts = [ ]
         self.messageDecls = [ ]
         self.transitionStmts = [ ]
         self.startStates = [ ]
 
 class StructField(Node):
     def __init__(self, loc, type, name):
@@ -299,16 +305,22 @@ class SpawnsStmt(Node):
         self.spawnedAs = spawnedAs
 
 class BridgesStmt(Node):
     def __init__(self, loc, parentSide, childSide):
         Node.__init__(self, loc)
         self.parentSide = parentSide
         self.childSide = childSide
 
+class OpensStmt(Node):
+    def __init__(self, loc, side, proto):
+        Node.__init__(self, loc)
+        self.side = side
+        self.proto = proto
+
 class Manager(Node):
     def __init__(self, loc, managerName):
         Node.__init__(self, loc)
         self.name = managerName
 
 class ManagesStmt(Node):
     def __init__(self, loc, managedName):
         Node.__init__(self, loc)
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -150,16 +150,17 @@ reserved = set((
         '__delete__',
         'delete',                       # reserve 'delete' to prevent its use
         'goto',
         'include',
         'manager',
         'manages',
         'namespace',
         'nullable',
+        'opens',
         'or',
         'parent',
         'protocol',
         'recv',
         'returns',
         'rpc',
         'send',
         'spawns',
@@ -338,17 +339,17 @@ def p_ProtocolDefn(p):
     protocol.sendSemantics = p[1]
     p[0] = protocol
 
 def p_ProtocolBody(p):
     """ProtocolBody : SpawnsStmtsOpt"""
     p[0] = p[1]
 
 ##--------------------
-## spawns/bridges stmts
+## spawns/bridges/opens stmts
 
 def p_SpawnsStmtsOpt(p):
     """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt
                       | BridgesStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
     else:
         p[2].spawnsStmts.insert(0, p[1])
@@ -365,27 +366,41 @@ def p_AsOpt(p):
              | """
     if 3 == len(p):
         p[0] = p[2]
     else:
         p[0] = 'child'
 
 def p_BridgesStmtsOpt(p):
     """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt
-                       | ManagersStmtOpt"""
+                       | OpensStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
     else:
         p[2].bridgesStmts.insert(0, p[1])
         p[0] = p[2]
 
 def p_BridgesStmt(p):
     """BridgesStmt : BRIDGES ID ',' ID ';'"""
     p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4])
 
+def p_OpensStmtsOpt(p):
+    """OpensStmtsOpt : OpensStmt OpensStmtsOpt
+                     | ManagersStmtOpt"""
+    if 2 == len(p):
+        p[0] = p[1]
+    else:
+        p[2].opensStmts.insert(0, p[1])
+        p[0] = p[2]
+
+def p_OpensStmt(p):
+    """OpensStmt : PARENT OPENS ID ';'
+                 | CHILD OPENS ID ';'"""
+    p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3])
+
 ##--------------------
 ## manager/manages stmts
 
 def p_ManagersStmtOpt(p):
     """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt
                        | ManagesStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -289,16 +289,17 @@ class Bridge:
     def __hash__(self):
         return hash(self.parent) + hash(self.child)
 
 class ProtocolType(IPDLType):
     def __init__(self, qname, sendSemantics, stateless=False):
         self.qname = qname
         self.sendSemantics = sendSemantics
         self.spawns = set()             # ProtocolType
+        self.opens = set()              # ProtocolType
         self.managers = set()           # ProtocolType
         self.manages = [ ]
         self.stateless = stateless
         self.hasDelete = False
     def isProtocol(self): return True
 
     def name(self):
         return self.qname.baseid
@@ -308,16 +309,20 @@ class ProtocolType(IPDLType):
     def addManager(self, mgrtype):
         assert mgrtype.isIPDL() and mgrtype.isProtocol()
         self.managers.add(mgrtype)
 
     def addSpawn(self, ptype):
         assert self.isToplevel() and  ptype.isToplevel()
         self.spawns.add(ptype)
 
+    def addOpen(self, ptype):
+        assert self.isToplevel() and  ptype.isToplevel()
+        self.opens.add(ptype)
+
     def managedBy(self, mgr):
         self.managers = mgr
 
     def toplevel(self):
         if self.isToplevel():
             return self
         for mgr in self.managers:
             if mgr is not self:
@@ -768,16 +773,19 @@ class GatherDecls(TcheckVisitor):
         self.symtab.enterScope(p)
 
         for spawns in p.spawnsStmts:
             spawns.accept(self)
 
         for bridges in p.bridgesStmts:
             bridges.accept(self)
 
+        for opens in p.opensStmts:
+            opens.accept(self)
+
         seenmgrs = set()
         for mgr in p.managers:
             if mgr.name in seenmgrs:
                 self.error(mgr.loc, "manager `%s' appears multiple times",
                            mgr.name)
                 continue
 
             seenmgrs.add(mgr.name)
@@ -918,16 +926,24 @@ class GatherDecls(TcheckVisitor):
             decl = self.symtab.lookup(p)
             if decl is None:
                 self.error(bridges.loc,
                            "bridged protocol `%s' has not been declared", p)
             return decl
         bridges.parentSide = lookup(bridges.parentSide)
         bridges.childSide = lookup(bridges.childSide)
 
+    def visitOpensStmt(self, opens):
+        pname = opens.proto
+        opens.proto = self.symtab.lookup(pname)
+        if opens.proto is None:
+            self.error(opens.loc,
+                       "opened protocol `%s' has not been declared",
+                       pname)
+
 
     def visitManager(self, mgr):
         mgrdecl = self.symtab.lookup(mgr.name)
         pdecl = mgr.of.decl
         assert pdecl
 
         pname, mgrname = pdecl.shortname, mgr.name
         loc = mgr.loc
@@ -1225,16 +1241,21 @@ class CheckTypes(TcheckVisitor):
                        "protocol `%s' is not top-level and so cannot declare |spawns|",
                        pname)
 
         if len(p.bridgesStmts) and not ptype.isToplevel():
             self.error(p.decl.loc,
                        "protocol `%s' is not top-level and so cannot declare |bridges|",
                        pname)
 
+        if len(p.opensStmts) and not ptype.isToplevel():
+            self.error(p.decl.loc,
+                       "protocol `%s' is not top-level and so cannot declare |opens|",
+                       pname)
+
         for mgrtype in ptype.managers:
             if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
                 self.error(
                     p.decl.loc,
                     "protocol `%s' requires more powerful send semantics than its manager `%s' provides",
                     pname, mgrtype.name())
 
         # XXX currently we don't require a delete() message of top-level
@@ -1293,16 +1314,33 @@ class CheckTypes(TcheckVisitor):
         if not (parentType.isIPDL() and parentType.isProtocol()
                 and childType.isIPDL() and childType.isProtocol()
                 and parentType.isToplevel() and childType.isToplevel()):
             self.error(bridges.loc,
                        "cannot bridge non-top-level-protocol(s) `%s' and `%s'",
                        parentType.name(), childType.name())
 
 
+    def visitOpensStmt(self, opens):
+        if not self.ptype.isToplevel():
+            self.error(opens.loc,
+                       "only top-level protocols can have |opens| statements; `%s' cannot",
+                       self.ptype.name())
+            return
+
+        openedType = opens.proto.type
+        if not (openedType.isIPDL() and openedType.isProtocol()
+                and openedType.isToplevel()):
+            self.error(opens.loc,
+                       "cannot open non-top-level-protocol `%s'",
+                       openedType.name())
+        else:
+            self.ptype.addOpen(openedType)
+
+
     def visitManagesStmt(self, mgs):
         pdecl = mgs.manager.decl
         ptype, pname = pdecl.type, pdecl.shortname
 
         mgsdecl = mgs.decl
         mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
 
         loc = mgs.loc
@@ -1462,21 +1500,30 @@ class BridgeEdge:
         self.bridgeProto = bridgeProto # ProtocolType
         self.parent = parent           # Actor
         self.child = child             # Actor
     def __repr__(self):
         return '(%r)--%s bridge-->(%r)'% (
             self.parent, self.bridgeProto.name(), self.child)
     def __str__(self):  return repr(self)
 
+class OpensEdge:
+    def __init__(self, opener, openedProto):
+        self.opener = opener            # Actor
+        self.openedProto = openedProto  # ProtocolType
+    def __repr__(self):
+        return '(%r)--opens-->(%s)'% (self.opener, self.openedProto.name())
+    def __str__(self):  return repr(self)
+
 # "singleton" class with state that persists across type checking of
 # all protocols
 class ProcessGraph:
     processes = set()                   # set(Process)
     bridges = { }                       # ProtocolType -> [ BridgeEdge ]
+    opens = { }                         # ProtocolType -> [ OpensEdge ]
     actorToProcess = { }                # Actor -> Process
     visitedSpawns = set()               # set(ActorType)
     visitedBridges = set()              # set(ActorType)
 
     @classmethod
     def findProcess(cls, actor):
         return cls.actorToProcess.get(actor, None)
 
@@ -1506,28 +1553,66 @@ class ProcessGraph:
 
     @classmethod
     def iterbridges(cls):
         for edges in cls.bridges.itervalues():
             for bridge in edges:
                 yield bridge
 
     @classmethod
+    def opensOf(cls, openedP):
+        return cls.opens.get(openedP, [])
+
+    @classmethod
+    def opensEndpointsOf(cls, ptype, side):
+        actor = Actor(ptype, side)
+        endpoints = []
+        for o in cls.iteropens():
+            if actor == o.opener:
+                endpoints.append(Actor(o.openedProto, o.opener.side))
+            elif actor == o.opener.other():
+                endpoints.append(Actor(o.openedProto, o.opener.other().side))
+        return endpoints
+
+    @classmethod
+    def iteropens(cls):
+        for edges in cls.opens.itervalues():
+            for opens in edges:
+                yield opens
+
+    @classmethod
     def spawn(cls, spawner, remoteSpawn):
         localSpawn = remoteSpawn.other()
         spawnerProcess = ProcessGraph.getProcess(spawner)
         spawnerProcess.merge(ProcessGraph.getProcess(localSpawn))
         spawnerProcess.edge(spawner, remoteSpawn)
 
     @classmethod
     def bridge(cls, parent, child, bridgeP):
+        bridgeParent = Actor(bridgeP, 'parent')
+        parentProcess = ProcessGraph.getProcess(parent)
+        parentProcess.merge(ProcessGraph.getProcess(bridgeParent))
+        bridgeChild = Actor(bridgeP, 'child')
+        childProcess = ProcessGraph.getProcess(child)
+        childProcess.merge(ProcessGraph.getProcess(bridgeChild))
         if bridgeP not in cls.bridges:
             cls.bridges[bridgeP] = [ ]
         cls.bridges[bridgeP].append(BridgeEdge(bridgeP, parent, child))
 
+    @classmethod
+    def open(cls, opener, opened, openedP):
+        remoteOpener, remoteOpened, = opener.other(), opened.other()
+        openerProcess = ProcessGraph.getProcess(opener)
+        openerProcess.merge(ProcessGraph.getProcess(opened))
+        remoteOpenerProcess = ProcessGraph.getProcess(remoteOpener)
+        remoteOpenerProcess.merge(ProcessGraph.getProcess(remoteOpened))
+        if openedP not in cls.opens:
+            cls.opens[openedP] = [ ]
+        cls.opens[openedP].append(OpensEdge(opener, openedP))
+
 
 class BuildProcessGraph(TcheckVisitor):
     class findSpawns(TcheckVisitor):
         def __init__(self, errors):
             TcheckVisitor.__init__(self, None, errors)
 
         def visitTranslationUnit(self, tu):
             TcheckVisitor.visitTranslationUnit(self, tu)
@@ -1624,16 +1709,32 @@ class BuildProcessGraph(TcheckVisitor):
         if parentSideActor is None:
             self.error(bridges.loc,
                        "`%s' and `%s' cannot be bridged by `%s' ",
                        parentSideProto.name(), childSideProto.name(),
                        bridgeProto.name())
 
         ProcessGraph.bridge(parentSideActor, childSideActor, bridgeProto)
 
+    def visitOpensStmt(self, opens):
+        openedP = opens.proto.type
+        opener = Actor(self.visiting, opens.side)
+        opened = Actor(openedP, opens.side)
+
+        # The picture here is:
+        #  [ opener       | opened ]   (process 1)
+        #      |               |
+        #      |               |
+        #  [ remoteOpener | remoteOpened ]  (process 2)
+        #
+        # An opens stmt tells us that the pairs |opener|/|opened|
+        # and |remoteOpener|/|remoteOpened| are each in the same
+        # process.
+        ProcessGraph.open(opener, opened, openedP)
+
 
 class CheckProcessGraph(TcheckVisitor):
     def __init__(self, errors):
         TcheckVisitor.__init__(self, None, errors)
 
     # TODO: verify spawns-per-process assumption and check that graph
     # is a dag
     def visitTranslationUnit(self, tu):
@@ -1642,16 +1743,20 @@ class CheckProcessGraph(TcheckVisitor):
             for process in ProcessGraph.processes:
                 print '  ', process
                 for edge in process.iteredges():
                     print '    ', edge
             print 'Bridges'
             for bridgeList in ProcessGraph.bridges.itervalues():
                 for bridge in bridgeList:
                     print '  ', bridge
+            print 'Opens'
+            for opensList in ProcessGraph.opens.itervalues():
+                for opens in opensList:
+                    print '  ', opens
 
 ##-----------------------------------------------------------------------------
 
 class CheckStateMachine(TcheckVisitor):
     def __init__(self, errors):
         # don't need the symbol table, we just want the error reporting
         TcheckVisitor.__init__(self, None, errors)
         self.p = None