Bug 521898, part 8: Frontend support for .ipdlh. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Fri, 08 Jun 2012 17:25:36 -0700
changeset 96230 1b4f5ef66e6edd7ab1501d45103a47cf59909960
parent 96229 7befc650f055b8fd50f1cb3d0422e7450083e3c1
child 96231 02d6424b10b801c16ceae899a3b2955f1ffb2e22
push id22883
push userryanvm@gmail.com
push dateSun, 10 Jun 2012 02:04:57 +0000
treeherdermozilla-central@dc410944aabc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs521898
milestone16.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 521898, part 8: Frontend support for .ipdlh. r=bent
ipc/ipdl/ipdl/__init__.py
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -13,17 +13,25 @@ from ipdl.parser import Parser
 from ipdl.type import TypeCheck
 
 from ipdl.cxx.cgen import CxxCodeGen
 
 
 def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr):
     '''Return an IPDL AST if parsing was successful.  Print errors to |errout|
     if it is not.'''
-    return Parser().parse(specstring, os.path.abspath(filename), includedirs, errout)
+    # The file type and name are later enforced by the type checker.
+    # This is just a hint to the parser.
+    prefix, ext = os.path.splitext(filename)
+    name = os.path.basename(prefix)
+    if ext == '.ipdlh':
+        type = 'header'
+    else:
+        type = 'protocol'
+    return Parser(type, name).parse(specstring, os.path.abspath(filename), includedirs, errout)
 
 
 def typecheck(ast, errout=sys.stderr):
     '''Return True iff |ast| is well typed.  Print errors to |errout| if
     it is not.'''
     return TypeCheck().check(ast, errout)
 
 
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -11,16 +11,18 @@ class Visitor:
 
     def visitTranslationUnit(self, tu):
         for cxxInc in tu.cxxIncludes:
             cxxInc.accept(self)
         for inc in tu.includes:
             inc.accept(self)
         for su in tu.structsAndUnions:
             su.accept(self)
+        for using in tu.builtinUsing:
+            using.accept(self)
         for using in tu.using:
             using.accept(self)
         if tu.protocol:
             tu.protocol.accept(self)
 
 
     def visitCxxInclude(self, inc):
         pass
@@ -146,21 +148,23 @@ class NamespacedNode(Node):
     def addOuterNamespace(self, namespace):
         self.namespaces.insert(0, namespace)
 
     def qname(self):
         return QualifiedId(self.loc, self.name,
                            [ ns.name for ns in self.namespaces ])
 
 class TranslationUnit(NamespacedNode):
-    def __init__(self, name):
+    def __init__(self, type, name):
         NamespacedNode.__init__(self, name=name)
+        self.filetype = type
         self.filename = None
         self.cxxIncludes = [ ]
         self.includes = [ ]
+        self.builtinUsing = [ ]
         self.using = [ ]
         self.structsAndUnions = [ ]
         self.protocol = None
 
     def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
     def addInclude(self, inc): self.includes.append(inc)
     def addStructDecl(self, struct): self.structsAndUnions.append(struct)
     def addUnionDecl(self, union): self.structsAndUnions.append(union)
@@ -169,19 +173,22 @@ class TranslationUnit(NamespacedNode):
     def setProtocol(self, protocol): self.protocol = protocol
 
 class CxxInclude(Node):
     def __init__(self, loc, cxxFile):
         Node.__init__(self, loc)
         self.file = cxxFile
 
 class Include(Node):
-    def __init__(self, loc, name):
+    def __init__(self, loc, type, name):
         Node.__init__(self, loc)
-        self.file = "%s.ipdl" % name
+        suffix = 'ipdl'
+        if type == 'header':
+            suffix += 'h'
+        self.file = "%s.%s" % (name, suffix)
 
 class UsingStmt(Node):
     def __init__(self, loc, cxxTypeSpec):
         Node.__init__(self, loc)
         self.type = cxxTypeSpec
 
 # "singletons"
 class ASYNC:
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -27,33 +27,35 @@ def _safeLinenoValue(t):
     if hasattr(t, 'lineno'): lineno = t.lineno
     if hasattr(t, 'value'):  value = t.value
     return lineno, value
 
 def _error(loc, fmt, *args):
     raise ParseError(loc, fmt, *args)
 
 class Parser:
-    # when we reach an |include protocol "foo.ipdl";| statement, we need to
+    # when we reach an |include [protocol] foo;| statement, we need to
     # save the current parser state and create a new one.  this "stack" is
     # where that state is saved
     #
     # there is one Parser per file
     current = None
     parseStack = [ ]
     parsed = { }
 
-    def __init__(self, debug=0):
+    def __init__(self, type, name, debug=0):
+        assert type and name
+        self.type = type
         self.debug = debug
         self.filename = None
         self.includedirs = None
         self.loc = None         # not always up to date
         self.lexer = None
         self.parser = None
-        self.tu = TranslationUnit('(none)')
+        self.tu = TranslationUnit(type, name)
         self.direction = None
         self.errout = None
 
     def parse(self, input, filename, includedirs, errout):
         assert os.path.isabs(filename)
 
         if filename in Parser.parsed:
             return Parser.parsed[filename].tu
@@ -177,16 +179,17 @@ def t_error(t):
     _error(Loc(Parser.current.filename, t.lineno),
            'lexically invalid characters `%s', t.value)
 
 ##-----------------------------------------------------------------------------
 
 def p_TranslationUnit(p):
     """TranslationUnit : Preamble NamespacedStuff"""
     tu = Parser.current.tu
+    tu.loc = Loc(tu.filename)
     for stmt in p[1]:
         if isinstance(stmt, CxxInclude):
             tu.addCxxInclude(stmt)
         elif isinstance(stmt, Include):
             tu.addInclude(stmt)
         elif isinstance(stmt, UsingStmt):
             tu.addUsingStmt(stmt)
         else:
@@ -202,59 +205,71 @@ def p_TranslationUnit(p):
                 _error(thing.loc, "only one protocol definition per file")
             tu.protocol = thing
         else:
             assert(0)
 
     # The "canonical" namespace of the tu, what it's considered to be
     # in for the purposes of C++: |#include "foo/bar/TU.h"|
     if tu.protocol:
+        assert tu.filetype == 'protocol'
         tu.namespaces = tu.protocol.namespaces
         tu.name = tu.protocol.name
+    else:
+        assert tu.filetype == 'header'
+        # There's not really a canonical "thing" in headers.  So
+        # somewhat arbitrarily use the namespace of the last
+        # interesting thing that was declared.
+        for thing in reversed(tu.structsAndUnions):
+            tu.namespaces = thing.namespaces
+            break
 
     p[0] = tu
 
 ##--------------------
 ## Preamble
 def p_Preamble(p):
     """Preamble : Preamble PreambleStmt ';'
                 |"""
     if 1 == len(p):
         p[0] = [ ]
     else:
         p[1].append(p[2])
         p[0] = p[1]
 
 def p_PreambleStmt(p):
     """PreambleStmt : CxxIncludeStmt
-                    | ProtocolIncludeStmt
+                    | IncludeStmt
                     | UsingStmt"""
     p[0] = p[1]
 
 def p_CxxIncludeStmt(p):
     """CxxIncludeStmt : INCLUDE STRING"""
     p[0] = CxxInclude(locFromTok(p, 1), p[2])
 
-def p_ProtocolIncludeStmt(p):
-    """ProtocolIncludeStmt : INCLUDE PROTOCOL ID
-                           | INCLUDE PROTOCOL STRING"""
+def p_IncludeStmt(p):
+    """IncludeStmt : INCLUDE PROTOCOL ID
+                   | INCLUDE ID"""
     loc = locFromTok(p, 1)
-    
-    if 0 <= p[3].rfind('.ipdl'):
-        _error(loc, "`include protocol \"P.ipdl\"' syntax is obsolete.  Use `include protocol P' instead.")
-    
+ 
     Parser.current.loc = loc
-    inc = Include(loc, p[3])
+    if 4 == len(p):
+        id = p[3]
+        type = 'protocol'
+    else:
+        id = p[2]
+        type = 'header'
+    inc = Include(loc, type, id)
 
     path = Parser.current.resolveIncludePath(inc.file)
     if path is None:
-        raise ParseError(loc, "can't locate protocol include file `%s'"% (
+        raise ParseError(loc, "can't locate include file `%s'"% (
                 inc.file))
     
-    inc.tu = Parser().parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout)
+    inc.tu = Parser(type, id).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])
 
 ##--------------------
 ## Namespaced stuff
@@ -316,16 +331,20 @@ def p_ComponentTypes(p):
 def p_ProtocolDefn(p):
     """ProtocolDefn : OptionalSendSemanticsQual PROTOCOL ID '{' ProtocolBody '}' ';'"""
     protocol = p[5]
     protocol.loc = locFromTok(p, 2)
     protocol.name = p[3]
     protocol.sendSemantics = p[1]
     p[0] = protocol
 
+    if Parser.current.type == 'header':
+        _error(protocol.loc, 'can\'t define a protocol in a header.  Do it in a protocol spec instead.')
+
+
 def p_ProtocolBody(p):
     """ProtocolBody : SpawnsStmtsOpt"""
     p[0] = p[1]
 
 ##--------------------
 ## spawns/bridges/opens stmts
 
 def p_SpawnsStmtsOpt(p):
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -606,33 +606,35 @@ class GatherDecls(TcheckVisitor):
         if hasattr(tu, 'symtab'):
             return
         tu.symtab = SymbolTable(self.errors)
         savedSymtab = self.symtab
         self.symtab = tu.symtab
 
         # pretend like the translation unit "using"-ed these for the
         # sake of type checking and C++ code generation
-        tu.using = self.builtinUsing + tu.using
+        tu.builtinUsing = self.builtinUsing
+
+        # for everyone's sanity, enforce that the filename and tu name
+        # match
+        basefilename = os.path.basename(tu.filename)
+        expectedfilename = '%s.ipdl'% (tu.name)
+        if not tu.protocol:
+            # header
+            expectedfilename += 'h'
+        if basefilename != expectedfilename:
+            self.error(tu.loc,
+                       "expected file for translation unit `%s' to be named `%s'; instead it's named `%s'",
+                       tu.name, expectedfilename, basefilename)
 
         if tu.protocol:
             assert tu.name == tu.protocol.name
 
             p = tu.protocol
 
-            # for everyone's sanity, enforce that the filename and
-            # protocol name match
-            basefilename = os.path.basename(tu.filename)
-            expectedfilename = '%s.ipdl'% (p.name)
-
-            if basefilename != expectedfilename:
-                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 = p.qname()
             if 0 == len(qname.quals):
                 fullname = None
@@ -649,27 +651,33 @@ class GatherDecls(TcheckVisitor):
             # what friend decls we need in generated C++
             p.decl.type._ast = p
 
         # make sure we have decls for all dependent protocols
         for pinc in tu.includes:
             pinc.accept(self)
 
         # declare imported (and builtin) C++ types
+        for using in tu.builtinUsing:
+            using.accept(self)
         for using in tu.using:
             using.accept(self)
 
         # first pass to "forward-declare" all structs and unions in
         # order to support recursive definitions
         for su in tu.structsAndUnions:
             self.declareStructOrUnion(su)
 
         # second pass to check each definition
         for su in tu.structsAndUnions:
             su.accept(self)
+        for inc in tu.includes:
+            if inc.tu.filetype == 'header':
+                for su in inc.tu.structsAndUnions:
+                    su.accept(self)
 
         if tu.protocol:
             # grab symbols in the protocol itself
             p.accept(self)
 
 
         tu.type = VOID
 
@@ -707,18 +715,29 @@ class GatherDecls(TcheckVisitor):
         if inc.tu is None:
             self.error(
                 inc.loc,
                 "(type checking here will be unreliable because of an earlier error)")
             return
         inc.tu.accept(self)
         if inc.tu.protocol:
             self.symtab.declare(inc.tu.protocol.decl)
+        else:
+            # This is a header.  Import its "exported" globals into
+            # our scope.
+            for using in inc.tu.using:
+                using.accept(self)
+            for su in inc.tu.structsAndUnions:
+                self.declareStructOrUnion(su)
 
     def visitStructDecl(self, sd):
+        # If we've already processed this struct, don't do it again.
+        if hasattr(sd, 'symtab'):
+            return
+
         stype = sd.decl.type
 
         self.symtab.enterScope(sd)
 
         for f in sd.fields:
             ftypedecl = self.symtab.lookup(str(f.typespec))
             if ftypedecl is None:
                 self.error(f.loc, "field `%s' of struct `%s' has unknown type `%s'",
@@ -731,16 +750,20 @@ class GatherDecls(TcheckVisitor):
                 shortname=f.name,
                 fullname=None)
             stype.fields.append(f.decl.type)
 
         self.symtab.exitScope(sd)
 
     def visitUnionDecl(self, ud):
         utype = ud.decl.type
+
+        # If we've already processed this union, don't do it again.
+        if len(utype.components):
+            return
         
         for c in ud.components:
             cdecl = self.symtab.lookup(str(c))
             if cdecl is None:
                 self.error(c.loc, "unknown component type `%s' of union `%s'",
                            str(c), ud.name)
                 continue
             utype.components.append(self._canonicalType(cdecl.type, c))