1# vim: set ts=4 sw=4 tw=99 et: 2# This Source Code Form is subject to the terms of the Mozilla Public 3# License, v. 2.0. If a copy of the MPL was not distributed with this 4# file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6from __future__ import print_function 7 8import os 9import sys 10 11from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, StructDecl 12from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor 13from ipdl.ast import ASYNC, SYNC, INTR 14from ipdl.ast import IN, OUT, INOUT 15from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED 16import ipdl.builtin as builtin 17from ipdl.util import hash_str 18 19_DELETE_MSG = "__delete__" 20 21 22class TypeVisitor: 23 def __init__(self): 24 self.visited = set() 25 26 def defaultVisit(self, node, *args): 27 raise Exception( 28 "INTERNAL ERROR: no visitor for node type `%s'" % (node.__class__.__name__) 29 ) 30 31 def visitVoidType(self, v, *args): 32 pass 33 34 def visitImportedCxxType(self, t, *args): 35 pass 36 37 def visitMessageType(self, m, *args): 38 for param in m.params: 39 param.accept(self, *args) 40 for ret in m.returns: 41 ret.accept(self, *args) 42 if m.cdtype is not None: 43 m.cdtype.accept(self, *args) 44 45 def visitProtocolType(self, p, *args): 46 # NB: don't visit manager and manages. a naive default impl 47 # could result in an infinite loop 48 pass 49 50 def visitActorType(self, a, *args): 51 a.protocol.accept(self, *args) 52 53 def visitStructType(self, s, *args): 54 if s in self.visited: 55 return 56 57 self.visited.add(s) 58 for field in s.fields: 59 field.accept(self, *args) 60 61 def visitUnionType(self, u, *args): 62 if u in self.visited: 63 return 64 65 self.visited.add(u) 66 for component in u.components: 67 component.accept(self, *args) 68 69 def visitArrayType(self, a, *args): 70 a.basetype.accept(self, *args) 71 72 def visitMaybeType(self, m, *args): 73 m.basetype.accept(self, *args) 74 75 def visitUniquePtrType(self, m, *args): 76 m.basetype.accept(self, *args) 77 78 def visitShmemType(self, s, *args): 79 pass 80 81 def visitByteBufType(self, s, *args): 82 pass 83 84 def visitShmemChmodType(self, c, *args): 85 c.shmem.accept(self) 86 87 def visitFDType(self, s, *args): 88 pass 89 90 def visitEndpointType(self, s, *args): 91 pass 92 93 def visitManagedEndpointType(self, s, *args): 94 pass 95 96 97class Type: 98 def __cmp__(self, o): 99 return cmp(self.fullname(), o.fullname()) 100 101 def __eq__(self, o): 102 return self.__class__ == o.__class__ and self.fullname() == o.fullname() 103 104 def __hash__(self): 105 return hash_str(self.fullname()) 106 107 # Is this a C++ type? 108 def isCxx(self): 109 return False 110 111 # Is this an IPDL type? 112 113 def isIPDL(self): 114 return False 115 116 # Is this type neither compound nor an array? 117 118 def isAtom(self): 119 return False 120 121 def isRefcounted(self): 122 return False 123 124 def isUniquePtr(self): 125 return False 126 127 def typename(self): 128 return self.__class__.__name__ 129 130 def name(self): 131 raise NotImplementedError() 132 133 def fullname(self): 134 raise NotImplementedError() 135 136 def accept(self, visitor, *args): 137 visit = getattr(visitor, "visit" + self.__class__.__name__, None) 138 if visit is None: 139 return getattr(visitor, "defaultVisit")(self, *args) 140 return visit(self, *args) 141 142 143class VoidType(Type): 144 def isCxx(self): 145 return True 146 147 def isIPDL(self): 148 return False 149 150 def isAtom(self): 151 return True 152 153 def name(self): 154 return "void" 155 156 def fullname(self): 157 return "void" 158 159 160VOID = VoidType() 161 162# -------------------- 163 164 165class ImportedCxxType(Type): 166 def __init__(self, qname, refcounted, moveonly): 167 assert isinstance(qname, QualifiedId) 168 self.loc = qname.loc 169 self.qname = qname 170 self.refcounted = refcounted 171 self.moveonly = moveonly 172 173 def isCxx(self): 174 return True 175 176 def isAtom(self): 177 return True 178 179 def isRefcounted(self): 180 return self.refcounted 181 182 def isMoveonly(self): 183 return self.moveonly 184 185 def name(self): 186 return self.qname.baseid 187 188 def fullname(self): 189 return str(self.qname) 190 191 192# -------------------- 193 194 195class IPDLType(Type): 196 def isIPDL(self): 197 return True 198 199 def isMessage(self): 200 return False 201 202 def isProtocol(self): 203 return False 204 205 def isActor(self): 206 return False 207 208 def isStruct(self): 209 return False 210 211 def isUnion(self): 212 return False 213 214 def isArray(self): 215 return False 216 217 def isMaybe(self): 218 return False 219 220 def isAtom(self): 221 return True 222 223 def isCompound(self): 224 return False 225 226 def isShmem(self): 227 return False 228 229 def isByteBuf(self): 230 return False 231 232 def isFD(self): 233 return False 234 235 def isEndpoint(self): 236 return False 237 238 def isManagedEndpoint(self): 239 return False 240 241 def isAsync(self): 242 return self.sendSemantics == ASYNC 243 244 def isSync(self): 245 return self.sendSemantics == SYNC 246 247 def isInterrupt(self): 248 return self.sendSemantics is INTR 249 250 def hasReply(self): 251 return self.isSync() or self.isInterrupt() 252 253 def hasBaseType(self): 254 return False 255 256 @classmethod 257 def convertsTo(cls, lesser, greater): 258 def _unwrap(nr): 259 if isinstance(nr, dict): 260 return _unwrap(nr["nested"]) 261 elif isinstance(nr, int): 262 return nr 263 else: 264 raise ValueError("Got unexpected nestedRange value: %s" % nr) 265 266 lnr0, gnr0, lnr1, gnr1 = ( 267 _unwrap(lesser.nestedRange[0]), 268 _unwrap(greater.nestedRange[0]), 269 _unwrap(lesser.nestedRange[1]), 270 _unwrap(greater.nestedRange[1]), 271 ) 272 if lnr0 < gnr0 or lnr1 > gnr1: 273 return False 274 275 # Protocols that use intr semantics are not allowed to use 276 # message nesting. 277 if greater.isInterrupt() and lesser.nestedRange != (NOT_NESTED, NOT_NESTED): 278 return False 279 280 if lesser.isAsync(): 281 return True 282 elif lesser.isSync() and not greater.isAsync(): 283 return True 284 elif greater.isInterrupt(): 285 return True 286 287 return False 288 289 def needsMoreJuiceThan(self, o): 290 return not IPDLType.convertsTo(self, o) 291 292 293class MessageType(IPDLType): 294 def __init__( 295 self, 296 nested, 297 prio, 298 sendSemantics, 299 direction, 300 ctor=False, 301 dtor=False, 302 cdtype=None, 303 compress=False, 304 tainted=False, 305 ): 306 assert not (ctor and dtor) 307 assert not (ctor or dtor) or cdtype is not None 308 309 self.nested = nested 310 self.prio = prio 311 self.nestedRange = (nested, nested) 312 self.sendSemantics = sendSemantics 313 self.direction = direction 314 self.params = [] 315 self.returns = [] 316 self.ctor = ctor 317 self.dtor = dtor 318 self.cdtype = cdtype 319 self.compress = compress 320 self.tainted = tainted 321 322 def isMessage(self): 323 return True 324 325 def isCtor(self): 326 return self.ctor 327 328 def isDtor(self): 329 return self.dtor 330 331 def constructedType(self): 332 return self.cdtype 333 334 def isIn(self): 335 return self.direction is IN 336 337 def isOut(self): 338 return self.direction is OUT 339 340 def isInout(self): 341 return self.direction is INOUT 342 343 def hasReply(self): 344 return len(self.returns) or IPDLType.hasReply(self) 345 346 def hasImplicitActorParam(self): 347 return self.isCtor() or self.isDtor() 348 349 350class ProtocolType(IPDLType): 351 def __init__(self, qname, nested, sendSemantics, refcounted): 352 self.qname = qname 353 self.nestedRange = (NOT_NESTED, nested) 354 self.sendSemantics = sendSemantics 355 self.managers = [] # ProtocolType 356 self.manages = [] 357 self.hasDelete = False 358 self.refcounted = refcounted 359 360 def isProtocol(self): 361 return True 362 363 def isRefcounted(self): 364 return self.refcounted 365 366 def name(self): 367 return self.qname.baseid 368 369 def fullname(self): 370 return str(self.qname) 371 372 def addManager(self, mgrtype): 373 assert mgrtype.isIPDL() and mgrtype.isProtocol() 374 self.managers.append(mgrtype) 375 376 def managedBy(self, mgr): 377 self.managers = list(mgr) 378 379 def toplevel(self): 380 if self.isToplevel(): 381 return self 382 for mgr in self.managers: 383 if mgr is not self: 384 return mgr.toplevel() 385 386 def toplevels(self): 387 if self.isToplevel(): 388 return [self] 389 toplevels = list() 390 for mgr in self.managers: 391 if mgr is not self: 392 toplevels.extend(mgr.toplevels()) 393 return set(toplevels) 394 395 def isManagerOf(self, pt): 396 for managed in self.manages: 397 if pt is managed: 398 return True 399 return False 400 401 def isManagedBy(self, pt): 402 return pt in self.managers 403 404 def isManager(self): 405 return len(self.manages) > 0 406 407 def isManaged(self): 408 return 0 < len(self.managers) 409 410 def isToplevel(self): 411 return not self.isManaged() 412 413 def manager(self): 414 assert 1 == len(self.managers) 415 for mgr in self.managers: 416 return mgr 417 418 419class ActorType(IPDLType): 420 def __init__(self, protocol, nullable=False): 421 self.protocol = protocol 422 self.nullable = nullable 423 424 def isActor(self): 425 return True 426 427 def isRefcounted(self): 428 return self.protocol.isRefcounted() 429 430 def name(self): 431 return self.protocol.name() 432 433 def fullname(self): 434 return self.protocol.fullname() 435 436 437class _CompoundType(IPDLType): 438 def __init__(self): 439 self.defined = False # bool 440 self.mutualRec = set() # set(_CompoundType | ArrayType) 441 442 def isAtom(self): 443 return False 444 445 def isCompound(self): 446 return True 447 448 def itercomponents(self): 449 raise Exception('"pure virtual" method') 450 451 def mutuallyRecursiveWith(self, t, exploring=None): 452 """|self| is mutually recursive with |t| iff |self| and |t| 453 are in a cycle in the type graph rooted at |self|. This function 454 looks for such a cycle and returns True if found.""" 455 if exploring is None: 456 exploring = set() 457 458 if t.isAtom(): 459 return False 460 elif t is self or t in self.mutualRec: 461 return True 462 elif t.hasBaseType(): 463 isrec = self.mutuallyRecursiveWith(t.basetype, exploring) 464 if isrec: 465 self.mutualRec.add(t) 466 return isrec 467 elif t in exploring: 468 return False 469 470 exploring.add(t) 471 for c in t.itercomponents(): 472 if self.mutuallyRecursiveWith(c, exploring): 473 self.mutualRec.add(c) 474 return True 475 exploring.remove(t) 476 477 return False 478 479 480class StructType(_CompoundType): 481 def __init__(self, qname, fields): 482 _CompoundType.__init__(self) 483 self.qname = qname 484 self.fields = fields # [ Type ] 485 486 def isStruct(self): 487 return True 488 489 def itercomponents(self): 490 for f in self.fields: 491 yield f 492 493 def name(self): 494 return self.qname.baseid 495 496 def fullname(self): 497 return str(self.qname) 498 499 500class UnionType(_CompoundType): 501 def __init__(self, qname, components): 502 _CompoundType.__init__(self) 503 self.qname = qname 504 self.components = components # [ Type ] 505 506 def isUnion(self): 507 return True 508 509 def itercomponents(self): 510 for c in self.components: 511 yield c 512 513 def name(self): 514 return self.qname.baseid 515 516 def fullname(self): 517 return str(self.qname) 518 519 520class ArrayType(IPDLType): 521 def __init__(self, basetype): 522 self.basetype = basetype 523 524 def isAtom(self): 525 return False 526 527 def isArray(self): 528 return True 529 530 def hasBaseType(self): 531 return True 532 533 def name(self): 534 return self.basetype.name() + "[]" 535 536 def fullname(self): 537 return self.basetype.fullname() + "[]" 538 539 540class MaybeType(IPDLType): 541 def __init__(self, basetype): 542 self.basetype = basetype 543 544 def isAtom(self): 545 return False 546 547 def isMaybe(self): 548 return True 549 550 def hasBaseType(self): 551 return True 552 553 def name(self): 554 return self.basetype.name() + "?" 555 556 def fullname(self): 557 return self.basetype.fullname() + "?" 558 559 560class ShmemType(IPDLType): 561 def __init__(self, qname): 562 self.qname = qname 563 564 def isShmem(self): 565 return True 566 567 def name(self): 568 return self.qname.baseid 569 570 def fullname(self): 571 return str(self.qname) 572 573 574class ByteBufType(IPDLType): 575 def __init__(self, qname): 576 self.qname = qname 577 578 def isByteBuf(self): 579 return True 580 581 def name(self): 582 return self.qname.baseid 583 584 def fullname(self): 585 return str(self.qname) 586 587 588class FDType(IPDLType): 589 def __init__(self, qname): 590 self.qname = qname 591 592 def isFD(self): 593 return True 594 595 def name(self): 596 return self.qname.baseid 597 598 def fullname(self): 599 return str(self.qname) 600 601 602class EndpointType(IPDLType): 603 def __init__(self, qname, actor): 604 self.qname = qname 605 self.actor = actor 606 607 def isEndpoint(self): 608 return True 609 610 def name(self): 611 return self.qname.baseid 612 613 def fullname(self): 614 return str(self.qname) 615 616 617class ManagedEndpointType(IPDLType): 618 def __init__(self, qname, actor): 619 self.qname = qname 620 self.actor = actor 621 622 def isManagedEndpoint(self): 623 return True 624 625 def name(self): 626 return self.qname.baseid 627 628 def fullname(self): 629 return str(self.qname) 630 631 632class UniquePtrType(IPDLType): 633 def __init__(self, basetype): 634 self.basetype = basetype 635 636 def isAtom(self): 637 return False 638 639 def isUniquePtr(self): 640 return True 641 642 def hasBaseType(self): 643 return True 644 645 def name(self): 646 return "UniquePtr<" + self.basetype.name() + ">" 647 648 def fullname(self): 649 return "mozilla::UniquePtr<" + self.basetype.fullname() + ">" 650 651 652def iteractortypes(t, visited=None): 653 """Iterate over any actor(s) buried in |type|.""" 654 if visited is None: 655 visited = set() 656 657 # XXX |yield| semantics makes it hard to use TypeVisitor 658 if not t.isIPDL(): 659 return 660 elif t.isActor(): 661 yield t 662 elif t.hasBaseType(): 663 for actor in iteractortypes(t.basetype, visited): 664 yield actor 665 elif t.isCompound() and t not in visited: 666 visited.add(t) 667 for c in t.itercomponents(): 668 for actor in iteractortypes(c, visited): 669 yield actor 670 671 672def hasshmem(type): 673 """Return true iff |type| is shmem or has it buried within.""" 674 675 class found(BaseException): 676 pass 677 678 class findShmem(TypeVisitor): 679 def visitShmemType(self, s): 680 raise found() 681 682 try: 683 type.accept(findShmem()) 684 except found: 685 return True 686 return False 687 688 689# -------------------- 690_builtinloc = Loc("<builtin>", 0) 691 692 693def makeBuiltinUsing(tname): 694 quals = tname.split("::") 695 base = quals.pop() 696 quals = quals[0:] 697 return UsingStmt( 698 _builtinloc, TypeSpec(_builtinloc, QualifiedId(_builtinloc, base, quals)) 699 ) 700 701 702builtinUsing = [makeBuiltinUsing(t) for t in builtin.Types] 703builtinHeaderIncludes = [CxxInclude(_builtinloc, f) for f in builtin.HeaderIncludes] 704 705 706def errormsg(loc, fmt, *args): 707 while not isinstance(loc, Loc): 708 if loc is None: 709 loc = Loc.NONE 710 else: 711 loc = loc.loc 712 return "%s: error: %s" % (str(loc), fmt % args) 713 714 715# -------------------- 716 717 718class SymbolTable: 719 def __init__(self, errors): 720 self.errors = errors 721 self.scopes = [{}] # stack({}) 722 self.currentScope = self.scopes[0] 723 724 def enterScope(self): 725 assert isinstance(self.scopes[0], dict) 726 assert isinstance(self.currentScope, dict) 727 728 self.scopes.append({}) 729 self.currentScope = self.scopes[-1] 730 731 def exitScope(self): 732 symtab = self.scopes.pop() 733 assert self.currentScope is symtab 734 735 self.currentScope = self.scopes[-1] 736 737 assert isinstance(self.scopes[0], dict) 738 assert isinstance(self.currentScope, dict) 739 740 def lookup(self, sym): 741 # NB: since IPDL doesn't allow any aliased names of different types, 742 # it doesn't matter in which order we walk the scope chain to resolve 743 # |sym| 744 for scope in self.scopes: 745 decl = scope.get(sym, None) 746 if decl is not None: 747 return decl 748 return None 749 750 def declare(self, decl): 751 assert decl.progname or decl.shortname or decl.fullname 752 assert decl.loc 753 assert decl.type 754 755 def tryadd(name): 756 olddecl = self.lookup(name) 757 if olddecl is not None: 758 self.errors.append( 759 errormsg( 760 decl.loc, 761 "redeclaration of symbol `%s', first declared at %s", 762 name, 763 olddecl.loc, 764 ) 765 ) 766 return 767 self.currentScope[name] = decl 768 decl.scope = self.currentScope 769 770 if decl.progname: 771 tryadd(decl.progname) 772 if decl.shortname: 773 tryadd(decl.shortname) 774 if decl.fullname: 775 tryadd(decl.fullname) 776 777 778class TypeCheck: 779 """This pass sets the .decl attribute of AST nodes for which that is relevant; 780 a decl says where, with what type, and under what name(s) a node was 781 declared. 782 783 With this information, it type checks the AST.""" 784 785 def __init__(self): 786 # NB: no IPDL compile will EVER print a warning. A program has 787 # one of two attributes: it is either well typed, or not well typed. 788 self.errors = [] # [ string ] 789 790 def check(self, tu, errout=sys.stderr): 791 def runpass(tcheckpass): 792 tu.accept(tcheckpass) 793 if len(self.errors): 794 self.reportErrors(errout) 795 return False 796 return True 797 798 # tag each relevant node with "decl" information, giving type, name, 799 # and location of declaration 800 if not runpass(GatherDecls(builtinUsing, self.errors)): 801 return False 802 803 # now that the nodes have decls, type checking is much easier. 804 if not runpass(CheckTypes(self.errors)): 805 return False 806 807 return True 808 809 def reportErrors(self, errout): 810 for error in self.errors: 811 print(error, file=errout) 812 813 814class TcheckVisitor(Visitor): 815 def __init__(self, errors): 816 self.errors = errors 817 818 def error(self, loc, fmt, *args): 819 self.errors.append(errormsg(loc, fmt, *args)) 820 821 822class GatherDecls(TcheckVisitor): 823 def __init__(self, builtinUsing, errors): 824 TcheckVisitor.__init__(self, errors) 825 826 # |self.symtab| is the symbol table for the translation unit 827 # currently being visited 828 self.symtab = None 829 self.builtinUsing = builtinUsing 830 831 def declare( 832 self, loc, type, shortname=None, fullname=None, progname=None, attributes={} 833 ): 834 d = Decl(loc) 835 d.type = type 836 d.progname = progname 837 d.shortname = shortname 838 d.fullname = fullname 839 d.attributes = attributes 840 self.symtab.declare(d) 841 return d 842 843 # Check that only attributes allowed by an attribute spec are present 844 # within the given attribute dictionary. The spec value may be either 845 # `None`, for a valueless attribute, a list of valid attribute values, or a 846 # callable which returns a truthy value if the attribute is valid. 847 def checkAttributes(self, attributes, spec): 848 for attr in attributes.values(): 849 if attr.name not in spec: 850 self.error(attr.loc, "unknown attribute `%s'", attr.name) 851 continue 852 853 aspec = spec[attr.name] 854 if aspec is None: 855 if attr.value is not None: 856 self.error( 857 attr.loc, 858 "unexpected value for valueless attribute `%s'", 859 attr.name, 860 ) 861 elif isinstance(aspec, (list, tuple)): 862 if attr.value not in aspec: 863 self.error( 864 attr.loc, 865 "invalid value for attribute `%s', expected one of: %s", 866 attr.name, 867 ", ".join(str(s) for s in aspec), 868 ) 869 elif callable(aspec): 870 if not aspec(attr.value): 871 self.error(attr.loc, "invalid value for attribute `%s'", attr.name) 872 else: 873 raise Exception("INTERNAL ERROR: Invalid attribute spec") 874 875 def visitTranslationUnit(self, tu): 876 # all TranslationUnits declare symbols in global scope 877 if hasattr(tu, "visited"): 878 return 879 tu.visited = True 880 savedSymtab = self.symtab 881 self.symtab = SymbolTable(self.errors) 882 883 # pretend like the translation unit "using"-ed these for the 884 # sake of type checking and C++ code generation 885 tu.builtinUsing = self.builtinUsing 886 887 # for everyone's sanity, enforce that the filename and tu name 888 # match 889 basefilename = os.path.basename(tu.filename) 890 expectedfilename = "%s.ipdl" % (tu.name) 891 if not tu.protocol: 892 # header 893 expectedfilename += "h" 894 if basefilename != expectedfilename: 895 self.error( 896 tu.loc, 897 "expected file for translation unit `%s' to be named `%s'; instead it's named `%s'", # NOQA: E501 898 tu.name, 899 expectedfilename, 900 basefilename, 901 ) 902 903 if tu.protocol: 904 assert tu.name == tu.protocol.name 905 906 p = tu.protocol 907 908 # FIXME/cjones: it's a little weird and counterintuitive 909 # to put both the namespace and non-namespaced name in the 910 # global scope. try to figure out something better; maybe 911 # a type-neutral |using| that works for C++ and protocol 912 # types? 913 qname = p.qname() 914 fullname = str(qname) 915 p.decl = self.declare( 916 loc=p.loc, 917 type=ProtocolType(qname, p.nested, p.sendSemantics, p.refcounted), 918 shortname=p.name, 919 fullname=None if 0 == len(qname.quals) else fullname, 920 ) 921 922 p.parentEndpointDecl = self.declare( 923 loc=p.loc, 924 type=EndpointType( 925 QualifiedId( 926 p.loc, "Endpoint<" + fullname + "Parent>", ["mozilla", "ipc"] 927 ), 928 ActorType(p.decl.type), 929 ), 930 shortname="Endpoint<" + p.name + "Parent>", 931 ) 932 p.childEndpointDecl = self.declare( 933 loc=p.loc, 934 type=EndpointType( 935 QualifiedId( 936 p.loc, "Endpoint<" + fullname + "Child>", ["mozilla", "ipc"] 937 ), 938 ActorType(p.decl.type), 939 ), 940 shortname="Endpoint<" + p.name + "Child>", 941 ) 942 943 p.parentManagedEndpointDecl = self.declare( 944 loc=p.loc, 945 type=ManagedEndpointType( 946 QualifiedId( 947 p.loc, 948 "ManagedEndpoint<" + fullname + "Parent>", 949 ["mozilla", "ipc"], 950 ), 951 ActorType(p.decl.type), 952 ), 953 shortname="ManagedEndpoint<" + p.name + "Parent>", 954 ) 955 p.childManagedEndpointDecl = self.declare( 956 loc=p.loc, 957 type=ManagedEndpointType( 958 QualifiedId( 959 p.loc, 960 "ManagedEndpoint<" + fullname + "Child>", 961 ["mozilla", "ipc"], 962 ), 963 ActorType(p.decl.type), 964 ), 965 shortname="ManagedEndpoint<" + p.name + "Child>", 966 ) 967 968 # XXX ugh, this sucks. but we need this information to compute 969 # what friend decls we need in generated C++ 970 p.decl.type._ast = p 971 972 # make sure we have decls for all dependent protocols 973 for pinc in tu.includes: 974 pinc.accept(self) 975 976 # declare imported (and builtin) C++ types 977 for using in tu.builtinUsing: 978 using.accept(self) 979 for using in tu.using: 980 using.accept(self) 981 982 # first pass to "forward-declare" all structs and unions in 983 # order to support recursive definitions 984 for su in tu.structsAndUnions: 985 self.declareStructOrUnion(su) 986 987 # second pass to check each definition 988 for su in tu.structsAndUnions: 989 su.accept(self) 990 991 if tu.protocol: 992 # grab symbols in the protocol itself 993 p.accept(self) 994 995 self.symtab = savedSymtab 996 997 def declareStructOrUnion(self, su): 998 if hasattr(su, "decl"): 999 self.symtab.declare(su.decl) 1000 return 1001 1002 qname = su.qname() 1003 if 0 == len(qname.quals): 1004 fullname = None 1005 else: 1006 fullname = str(qname) 1007 1008 if isinstance(su, StructDecl): 1009 sutype = StructType(qname, []) 1010 elif isinstance(su, UnionDecl): 1011 sutype = UnionType(qname, []) 1012 else: 1013 assert 0 and "unknown type" 1014 1015 # XXX more suckage. this time for pickling structs/unions 1016 # declared in headers. 1017 sutype._ast = su 1018 1019 su.decl = self.declare( 1020 loc=su.loc, type=sutype, shortname=su.name, fullname=fullname 1021 ) 1022 1023 def visitInclude(self, inc): 1024 if inc.tu is None: 1025 self.error( 1026 inc.loc, 1027 "(type checking here will be unreliable because of an earlier error)", 1028 ) 1029 return 1030 inc.tu.accept(self) 1031 if inc.tu.protocol: 1032 self.symtab.declare(inc.tu.protocol.decl) 1033 self.symtab.declare(inc.tu.protocol.parentEndpointDecl) 1034 self.symtab.declare(inc.tu.protocol.childEndpointDecl) 1035 self.symtab.declare(inc.tu.protocol.parentManagedEndpointDecl) 1036 self.symtab.declare(inc.tu.protocol.childManagedEndpointDecl) 1037 else: 1038 # This is a header. Import its "exported" globals into 1039 # our scope. 1040 for using in inc.tu.using: 1041 using.accept(self) 1042 for su in inc.tu.structsAndUnions: 1043 self.declareStructOrUnion(su) 1044 1045 def visitStructDecl(self, sd): 1046 # If we've already processed this struct, don't do it again. 1047 if hasattr(sd, "visited"): 1048 return 1049 1050 stype = sd.decl.type 1051 1052 self.symtab.enterScope() 1053 sd.visited = True 1054 1055 self.checkAttributes(sd.attributes, {"Comparable": None}) 1056 1057 for f in sd.fields: 1058 ftypedecl = self.symtab.lookup(str(f.typespec)) 1059 if ftypedecl is None: 1060 self.error( 1061 f.loc, 1062 "field `%s' of struct `%s' has unknown type `%s'", 1063 f.name, 1064 sd.name, 1065 str(f.typespec), 1066 ) 1067 continue 1068 1069 f.decl = self.declare( 1070 loc=f.loc, 1071 type=self._canonicalType(ftypedecl.type, f.typespec), 1072 shortname=f.name, 1073 fullname=None, 1074 ) 1075 stype.fields.append(f.decl.type) 1076 1077 self.symtab.exitScope() 1078 1079 def visitUnionDecl(self, ud): 1080 utype = ud.decl.type 1081 1082 # If we've already processed this union, don't do it again. 1083 if len(utype.components): 1084 return 1085 1086 self.checkAttributes(ud.attributes, {"Comparable": None}) 1087 1088 for c in ud.components: 1089 cdecl = self.symtab.lookup(str(c)) 1090 if cdecl is None: 1091 self.error( 1092 c.loc, "unknown component type `%s' of union `%s'", str(c), ud.name 1093 ) 1094 continue 1095 utype.components.append(self._canonicalType(cdecl.type, c)) 1096 1097 def visitUsingStmt(self, using): 1098 fullname = str(using.type) 1099 if (using.type.basename() == fullname) or using.type.uniqueptr: 1100 # Prevent generation of typedefs. If basename == fullname then 1101 # there is nothing to typedef. With UniquePtrs, basenames 1102 # are generic so typedefs would be illegal. 1103 fullname = None 1104 1105 self.checkAttributes( 1106 using.attributes, 1107 { 1108 "MoveOnly": None, 1109 "RefCounted": None, 1110 }, 1111 ) 1112 1113 if fullname == "mozilla::ipc::Shmem": 1114 ipdltype = ShmemType(using.type.spec) 1115 elif fullname == "mozilla::ipc::ByteBuf": 1116 ipdltype = ByteBufType(using.type.spec) 1117 elif fullname == "mozilla::ipc::FileDescriptor": 1118 ipdltype = FDType(using.type.spec) 1119 else: 1120 ipdltype = ImportedCxxType( 1121 using.type.spec, using.isRefcounted(), using.isMoveonly() 1122 ) 1123 existingType = self.symtab.lookup(ipdltype.fullname()) 1124 if existingType and existingType.fullname == ipdltype.fullname(): 1125 if ipdltype.isRefcounted() != existingType.type.isRefcounted(): 1126 self.error( 1127 using.loc, 1128 "inconsistent refcounted status of type `%s`", 1129 str(using.type), 1130 ) 1131 if ipdltype.isMoveonly() != existingType.type.isMoveonly(): 1132 self.error( 1133 using.loc, 1134 "inconsistent moveonly status of type `%s`", 1135 str(using.type), 1136 ) 1137 using.decl = existingType 1138 return 1139 using.decl = self.declare( 1140 loc=using.loc, 1141 type=ipdltype, 1142 shortname=using.type.basename(), 1143 fullname=fullname, 1144 ) 1145 1146 def visitProtocol(self, p): 1147 # protocol scope 1148 self.symtab.enterScope() 1149 1150 seenmgrs = set() 1151 for mgr in p.managers: 1152 if mgr.name in seenmgrs: 1153 self.error(mgr.loc, "manager `%s' appears multiple times", mgr.name) 1154 continue 1155 1156 seenmgrs.add(mgr.name) 1157 mgr.of = p 1158 mgr.accept(self) 1159 1160 for managed in p.managesStmts: 1161 managed.manager = p 1162 managed.accept(self) 1163 1164 if not (p.managers or p.messageDecls or p.managesStmts): 1165 self.error(p.loc, "top-level protocol `%s' cannot be empty", p.name) 1166 1167 setattr(self, "currentProtocolDecl", p.decl) 1168 for msg in p.messageDecls: 1169 msg.accept(self) 1170 del self.currentProtocolDecl 1171 1172 p.decl.type.hasDelete = not not self.symtab.lookup(_DELETE_MSG) 1173 if not (p.decl.type.hasDelete or p.decl.type.isToplevel()): 1174 self.error( 1175 p.loc, 1176 "destructor declaration `%s(...)' required for managed protocol `%s'", 1177 _DELETE_MSG, 1178 p.name, 1179 ) 1180 1181 # FIXME/cjones declare all the little C++ thingies that will 1182 # be generated. they're not relevant to IPDL itself, but 1183 # those ("invisible") symbols can clash with others in the 1184 # IPDL spec, and we'd like to catch those before C++ compilers 1185 # are allowed to obfuscate the error 1186 1187 self.symtab.exitScope() 1188 1189 def visitManager(self, mgr): 1190 mgrdecl = self.symtab.lookup(mgr.name) 1191 pdecl = mgr.of.decl 1192 assert pdecl 1193 1194 pname, mgrname = pdecl.shortname, mgr.name 1195 loc = mgr.loc 1196 1197 if mgrdecl is None: 1198 self.error( 1199 loc, 1200 "protocol `%s' referenced as |manager| of `%s' has not been declared", 1201 mgrname, 1202 pname, 1203 ) 1204 elif not isinstance(mgrdecl.type, ProtocolType): 1205 self.error( 1206 loc, 1207 "entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'", # NOQA: E501 1208 mgrname, 1209 pname, 1210 mgrdecl.type.typename(), 1211 ) 1212 else: 1213 mgr.decl = mgrdecl 1214 pdecl.type.addManager(mgrdecl.type) 1215 1216 def visitManagesStmt(self, mgs): 1217 mgsdecl = self.symtab.lookup(mgs.name) 1218 pdecl = mgs.manager.decl 1219 assert pdecl 1220 1221 pname, mgsname = pdecl.shortname, mgs.name 1222 loc = mgs.loc 1223 1224 if mgsdecl is None: 1225 self.error( 1226 loc, 1227 "protocol `%s', managed by `%s', has not been declared", 1228 mgsname, 1229 pname, 1230 ) 1231 elif not isinstance(mgsdecl.type, ProtocolType): 1232 self.error( 1233 loc, 1234 "%s declares itself managing a non-`protocol' entity `%s' of type `%s'", 1235 pname, 1236 mgsname, 1237 mgsdecl.type.typename(), 1238 ) 1239 else: 1240 mgs.decl = mgsdecl 1241 pdecl.type.manages.append(mgsdecl.type) 1242 1243 def visitMessageDecl(self, md): 1244 msgname = md.name 1245 loc = md.loc 1246 1247 self.checkAttributes( 1248 md.attributes, 1249 { 1250 "Tainted": None, 1251 "Compress": (None, "all"), 1252 "Priority": ("normal", "input", "vsync", "mediumhigh", "control"), 1253 "Nested": ("not", "inside_sync", "inside_cpow"), 1254 }, 1255 ) 1256 1257 if md.sendSemantics is INTR and "Priority" in md.attributes: 1258 self.error(loc, "intr message `%s' cannot specify [Priority]", msgname) 1259 1260 if md.sendSemantics is INTR and "Nested" in md.attributes: 1261 self.error(loc, "intr message `%s' cannot specify [Nested]", msgname) 1262 1263 isctor = False 1264 isdtor = False 1265 cdtype = None 1266 1267 decl = self.symtab.lookup(msgname) 1268 if decl is not None and decl.type.isProtocol(): 1269 # probably a ctor. we'll check validity later. 1270 msgname += "Constructor" 1271 isctor = True 1272 cdtype = decl.type 1273 elif decl is not None: 1274 self.error( 1275 loc, 1276 "message name `%s' already declared as `%s'", 1277 msgname, 1278 decl.type.typename(), 1279 ) 1280 # if we error here, no big deal; move on to find more 1281 1282 if _DELETE_MSG == msgname: 1283 isdtor = True 1284 cdtype = self.currentProtocolDecl.type 1285 1286 # enter message scope 1287 self.symtab.enterScope() 1288 1289 msgtype = MessageType( 1290 md.nested(), 1291 md.priority(), 1292 md.sendSemantics, 1293 md.direction, 1294 ctor=isctor, 1295 dtor=isdtor, 1296 cdtype=cdtype, 1297 compress=md.attributes.get("Compress"), 1298 tainted="Tainted" in md.attributes, 1299 ) 1300 1301 # replace inparam Param nodes with proper Decls 1302 def paramToDecl(param): 1303 self.checkAttributes( 1304 param.attributes, 1305 { 1306 # Passback indicates that the argument is unused by the Parent and is 1307 # merely returned to the Child later. 1308 # AllValid indicates that the entire span of values representable by 1309 # the type are acceptable. e.g. 0-255 in a uint8 1310 "NoTaint": ("passback", "allvalid") 1311 }, 1312 ) 1313 1314 ptname = param.typespec.basename() 1315 ploc = param.typespec.loc 1316 1317 if "NoTaint" in param.attributes and "Tainted" not in md.attributes: 1318 self.error( 1319 ploc, 1320 "argument typename `%s' of message `%s' has a NoTaint attribute, but the message lacks the Tainted attribute", 1321 ptname, 1322 msgname, 1323 ) 1324 1325 ptdecl = self.symtab.lookup(ptname) 1326 if ptdecl is None: 1327 self.error( 1328 ploc, 1329 "argument typename `%s' of message `%s' has not been declared", 1330 ptname, 1331 msgname, 1332 ) 1333 ptype = VOID 1334 else: 1335 ptype = self._canonicalType(ptdecl.type, param.typespec) 1336 return self.declare( 1337 loc=ploc, type=ptype, progname=param.name, attributes=param.attributes 1338 ) 1339 1340 for i, inparam in enumerate(md.inParams): 1341 pdecl = paramToDecl(inparam) 1342 msgtype.params.append(pdecl.type) 1343 md.inParams[i] = pdecl 1344 for i, outparam in enumerate(md.outParams): 1345 pdecl = paramToDecl(outparam) 1346 msgtype.returns.append(pdecl.type) 1347 md.outParams[i] = pdecl 1348 1349 self.symtab.exitScope() 1350 1351 md.decl = self.declare(loc=loc, type=msgtype, progname=msgname) 1352 md.protocolDecl = self.currentProtocolDecl 1353 md.decl._md = md 1354 1355 def _canonicalType(self, itype, typespec): 1356 loc = typespec.loc 1357 if itype.isIPDL(): 1358 if itype.isProtocol(): 1359 itype = ActorType(itype, nullable=typespec.nullable) 1360 1361 if typespec.nullable and not (itype.isIPDL() and itype.isActor()): 1362 self.error( 1363 loc, "`nullable' qualifier for type `%s' makes no sense", itype.name() 1364 ) 1365 1366 if typespec.array: 1367 itype = ArrayType(itype) 1368 1369 if typespec.maybe: 1370 itype = MaybeType(itype) 1371 1372 if typespec.uniqueptr: 1373 itype = UniquePtrType(itype) 1374 1375 return itype 1376 1377 1378# ----------------------------------------------------------------------------- 1379 1380 1381def checkcycles(p, stack=None): 1382 cycles = [] 1383 1384 if stack is None: 1385 stack = [] 1386 1387 for cp in p.manages: 1388 # special case for self-managed protocols 1389 if cp is p: 1390 continue 1391 1392 if cp in stack: 1393 return [stack + [p, cp]] 1394 cycles += checkcycles(cp, stack + [p]) 1395 1396 return cycles 1397 1398 1399def formatcycles(cycles): 1400 r = [] 1401 for cycle in cycles: 1402 s = " -> ".join([ptype.name() for ptype in cycle]) 1403 r.append("`%s'" % s) 1404 return ", ".join(r) 1405 1406 1407def fullyDefined(t, exploring=None): 1408 """The rules for "full definition" of a type are 1409 defined(atom) := true 1410 defined(array basetype) := defined(basetype) 1411 defined(struct f1 f2...) := defined(f1) and defined(f2) and ... 1412 defined(union c1 c2 ...) := defined(c1) or defined(c2) or ...""" 1413 if exploring is None: 1414 exploring = set() 1415 1416 if t.isAtom(): 1417 return True 1418 elif t.hasBaseType(): 1419 return fullyDefined(t.basetype, exploring) 1420 elif t.defined: 1421 return True 1422 assert t.isCompound() 1423 1424 if t in exploring: 1425 return False 1426 1427 exploring.add(t) 1428 for c in t.itercomponents(): 1429 cdefined = fullyDefined(c, exploring) 1430 if t.isStruct() and not cdefined: 1431 t.defined = False 1432 break 1433 elif t.isUnion() and cdefined: 1434 t.defined = True 1435 break 1436 else: 1437 if t.isStruct(): 1438 t.defined = True 1439 elif t.isUnion(): 1440 t.defined = False 1441 exploring.remove(t) 1442 1443 return t.defined 1444 1445 1446class CheckTypes(TcheckVisitor): 1447 def __init__(self, errors): 1448 TcheckVisitor.__init__(self, errors) 1449 self.visited = set() 1450 self.ptype = None 1451 1452 def visitInclude(self, inc): 1453 if inc.tu.filename in self.visited: 1454 return 1455 self.visited.add(inc.tu.filename) 1456 if inc.tu.protocol: 1457 inc.tu.protocol.accept(self) 1458 1459 def visitStructDecl(self, sd): 1460 if not fullyDefined(sd.decl.type): 1461 self.error(sd.decl.loc, "struct `%s' is only partially defined", sd.name) 1462 1463 def visitUnionDecl(self, ud): 1464 if not fullyDefined(ud.decl.type): 1465 self.error(ud.decl.loc, "union `%s' is only partially defined", ud.name) 1466 1467 def visitProtocol(self, p): 1468 self.ptype = p.decl.type 1469 1470 # check that we require no more "power" than our manager protocols 1471 ptype, pname = p.decl.type, p.decl.shortname 1472 1473 for mgrtype in ptype.managers: 1474 if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype): 1475 self.error( 1476 p.decl.loc, 1477 "protocol `%s' requires more powerful send semantics than its manager `%s' provides", # NOQA: E501 1478 pname, 1479 mgrtype.name(), 1480 ) 1481 1482 if ptype.isToplevel(): 1483 cycles = checkcycles(p.decl.type) 1484 if cycles: 1485 self.error( 1486 p.decl.loc, 1487 "cycle(s) detected in manager/manages hierarchy: %s", 1488 formatcycles(cycles), 1489 ) 1490 1491 if 1 == len(ptype.managers) and ptype is ptype.manager(): 1492 self.error( 1493 p.decl.loc, "top-level protocol `%s' cannot manage itself", p.name 1494 ) 1495 1496 return Visitor.visitProtocol(self, p) 1497 1498 def visitManagesStmt(self, mgs): 1499 pdecl = mgs.manager.decl 1500 ptype, pname = pdecl.type, pdecl.shortname 1501 1502 mgsdecl = mgs.decl 1503 mgstype, mgsname = mgsdecl.type, mgsdecl.shortname 1504 1505 loc = mgs.loc 1506 1507 # we added this information; sanity check it 1508 assert ptype.isManagerOf(mgstype) 1509 1510 # check that the "managed" protocol agrees 1511 if not mgstype.isManagedBy(ptype): 1512 self.error( 1513 loc, 1514 "|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'", # NOQA: E501 1515 pname, 1516 mgsname, 1517 ) 1518 1519 def visitManager(self, mgr): 1520 pdecl = mgr.of.decl 1521 ptype, pname = pdecl.type, pdecl.shortname 1522 1523 mgrdecl = mgr.decl 1524 mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname 1525 1526 # we added this information; sanity check it 1527 assert ptype.isManagedBy(mgrtype) 1528 1529 loc = mgr.loc 1530 1531 # check that the "manager" protocol agrees 1532 if not mgrtype.isManagerOf(ptype): 1533 self.error( 1534 loc, 1535 "|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'", # NOQA: E501 1536 pname, 1537 mgrname, 1538 ) 1539 1540 def visitMessageDecl(self, md): 1541 mtype, mname = md.decl.type, md.decl.progname 1542 ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname 1543 1544 loc = md.decl.loc 1545 1546 if mtype.nested == INSIDE_SYNC_NESTED and not mtype.isSync(): 1547 self.error( 1548 loc, 1549 "inside_sync nested messages must be sync (here, message `%s' in protocol `%s')", 1550 mname, 1551 pname, 1552 ) 1553 1554 if mtype.nested == INSIDE_CPOW_NESTED and (mtype.isOut() or mtype.isInout()): 1555 self.error( 1556 loc, 1557 "inside_cpow nested parent-to-child messages are verboten (here, message `%s' in protocol `%s')", # NOQA: E501 1558 mname, 1559 pname, 1560 ) 1561 1562 # We allow inside_sync messages that are themselves sync to be sent from the 1563 # parent. Normal and inside_cpow nested messages that are sync can only come from 1564 # the child. 1565 if ( 1566 mtype.isSync() 1567 and mtype.nested == NOT_NESTED 1568 and (mtype.isOut() or mtype.isInout()) 1569 ): 1570 self.error( 1571 loc, 1572 "sync parent-to-child messages are verboten (here, message `%s' in protocol `%s')", 1573 mname, 1574 pname, 1575 ) 1576 1577 if mtype.needsMoreJuiceThan(ptype): 1578 self.error( 1579 loc, 1580 "message `%s' requires more powerful send semantics than its protocol `%s' provides", # NOQA: E501 1581 mname, 1582 pname, 1583 ) 1584 1585 if (mtype.isCtor() or mtype.isDtor()) and mtype.isAsync() and mtype.returns: 1586 self.error( 1587 loc, "asynchronous ctor/dtor message `%s' declares return values", mname 1588 ) 1589 1590 if mtype.compress and (not mtype.isAsync() or mtype.isCtor() or mtype.isDtor()): 1591 1592 if mtype.isCtor() or mtype.isDtor(): 1593 message_type = "constructor" if mtype.isCtor() else "destructor" 1594 error_message = ( 1595 "%s messages can't use compression (here, in protocol `%s')" 1596 % (message_type, pname) 1597 ) 1598 else: 1599 error_message = ( 1600 "message `%s' in protocol `%s' requests compression but is not async" 1601 % (mname, pname) # NOQA: E501 1602 ) 1603 1604 self.error(loc, error_message) 1605 1606 if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()): 1607 self.error( 1608 loc, 1609 "ctor for protocol `%s', which is not managed by protocol `%s'", 1610 mname[: -len("constructor")], 1611 pname, 1612 ) 1613