1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5""" A WebIDL parser. """ 6 7from __future__ import print_function 8from ply import lex, yacc 9import re 10import os 11import traceback 12import math 13import string 14from collections import defaultdict, OrderedDict 15from itertools import chain 16 17# Machinery 18 19 20def parseInt(literal): 21 string = literal 22 sign = 0 23 base = 0 24 25 if string[0] == "-": 26 sign = -1 27 string = string[1:] 28 else: 29 sign = 1 30 31 if string[0] == "0" and len(string) > 1: 32 if string[1] == "x" or string[1] == "X": 33 base = 16 34 string = string[2:] 35 else: 36 base = 8 37 string = string[1:] 38 else: 39 base = 10 40 41 value = int(string, base) 42 return value * sign 43 44 45def enum(*names, **kw): 46 class Foo(object): 47 attrs = OrderedDict() 48 49 def __init__(self, names): 50 for v, k in enumerate(names): 51 self.attrs[k] = v 52 53 def __getattr__(self, attr): 54 if attr in self.attrs: 55 return self.attrs[attr] 56 raise AttributeError 57 58 def __setattr__(self, name, value): # this makes it read-only 59 raise NotImplementedError 60 61 if "base" not in kw: 62 return Foo(names) 63 return Foo(chain(kw["base"].attrs.keys(), names)) 64 65 66class WebIDLError(Exception): 67 def __init__(self, message, locations, warning=False): 68 self.message = message 69 self.locations = [str(loc) for loc in locations] 70 self.warning = warning 71 72 def __str__(self): 73 return "%s: %s%s%s" % ( 74 self.warning and "warning" or "error", 75 self.message, 76 ", " if len(self.locations) != 0 else "", 77 "\n".join(self.locations), 78 ) 79 80 81class Location(object): 82 def __init__(self, lexer, lineno, lexpos, filename): 83 self._line = None 84 self._lineno = lineno 85 self._lexpos = lexpos 86 self._lexdata = lexer.lexdata 87 self._file = filename if filename else "<unknown>" 88 89 def __eq__(self, other): 90 return self._lexpos == other._lexpos and self._file == other._file 91 92 def filename(self): 93 return self._file 94 95 def resolve(self): 96 if self._line: 97 return 98 99 startofline = self._lexdata.rfind("\n", 0, self._lexpos) + 1 100 endofline = self._lexdata.find("\n", self._lexpos, self._lexpos + 80) 101 if endofline != -1: 102 self._line = self._lexdata[startofline:endofline] 103 else: 104 self._line = self._lexdata[startofline:] 105 self._colno = self._lexpos - startofline 106 107 # Our line number seems to point to the start of self._lexdata 108 self._lineno += self._lexdata.count("\n", 0, startofline) 109 110 def get(self): 111 self.resolve() 112 return "%s line %s:%s" % (self._file, self._lineno, self._colno) 113 114 def _pointerline(self): 115 return " " * self._colno + "^" 116 117 def __str__(self): 118 self.resolve() 119 return "%s line %s:%s\n%s\n%s" % ( 120 self._file, 121 self._lineno, 122 self._colno, 123 self._line, 124 self._pointerline(), 125 ) 126 127 128class BuiltinLocation(object): 129 def __init__(self, text): 130 self.msg = text + "\n" 131 132 def __eq__(self, other): 133 return isinstance(other, BuiltinLocation) and self.msg == other.msg 134 135 def filename(self): 136 return "<builtin>" 137 138 def resolve(self): 139 pass 140 141 def get(self): 142 return self.msg 143 144 def __str__(self): 145 return self.get() 146 147 148# Data Model 149 150 151class IDLObject(object): 152 def __init__(self, location): 153 self.location = location 154 self.userData = dict() 155 156 def filename(self): 157 return self.location.filename() 158 159 def isInterface(self): 160 return False 161 162 def isNamespace(self): 163 return False 164 165 def isInterfaceMixin(self): 166 return False 167 168 def isEnum(self): 169 return False 170 171 def isCallback(self): 172 return False 173 174 def isType(self): 175 return False 176 177 def isDictionary(self): 178 return False 179 180 def isUnion(self): 181 return False 182 183 def isTypedef(self): 184 return False 185 186 def getUserData(self, key, default): 187 return self.userData.get(key, default) 188 189 def setUserData(self, key, value): 190 self.userData[key] = value 191 192 def addExtendedAttributes(self, attrs): 193 assert False # Override me! 194 195 def handleExtendedAttribute(self, attr): 196 assert False # Override me! 197 198 def _getDependentObjects(self): 199 assert False # Override me! 200 201 def getDeps(self, visited=None): 202 """Return a set of files that this object depends on. If any of 203 these files are changed the parser needs to be rerun to regenerate 204 a new IDLObject. 205 206 The visited argument is a set of all the objects already visited. 207 We must test to see if we are in it, and if so, do nothing. This 208 prevents infinite recursion.""" 209 210 # NB: We can't use visited=set() above because the default value is 211 # evaluated when the def statement is evaluated, not when the function 212 # is executed, so there would be one set for all invocations. 213 if visited is None: 214 visited = set() 215 216 if self in visited: 217 return set() 218 219 visited.add(self) 220 221 deps = set() 222 if self.filename() != "<builtin>": 223 deps.add(self.filename()) 224 225 for d in self._getDependentObjects(): 226 deps.update(d.getDeps(visited)) 227 228 return deps 229 230 231class IDLScope(IDLObject): 232 def __init__(self, location, parentScope, identifier): 233 IDLObject.__init__(self, location) 234 235 self.parentScope = parentScope 236 if identifier: 237 assert isinstance(identifier, IDLIdentifier) 238 self._name = identifier 239 else: 240 self._name = None 241 242 self._dict = {} 243 self.globalNames = set() 244 # A mapping from global name to the set of global interfaces 245 # that have that global name. 246 self.globalNameMapping = defaultdict(set) 247 248 def __str__(self): 249 return self.QName() 250 251 def QName(self): 252 # It's possible for us to be called before __init__ has been called, for 253 # the IDLObjectWithScope case. In that case, self._name won't be set yet. 254 if hasattr(self, "_name"): 255 name = self._name 256 else: 257 name = None 258 if name: 259 return name.QName() + "::" 260 return "::" 261 262 def ensureUnique(self, identifier, object): 263 """ 264 Ensure that there is at most one 'identifier' in scope ('self'). 265 Note that object can be None. This occurs if we end up here for an 266 interface type we haven't seen yet. 267 """ 268 assert isinstance(identifier, IDLUnresolvedIdentifier) 269 assert not object or isinstance(object, IDLObjectWithIdentifier) 270 assert not object or object.identifier == identifier 271 272 if identifier.name in self._dict: 273 if not object: 274 return 275 276 # ensureUnique twice with the same object is not allowed 277 assert id(object) != id(self._dict[identifier.name]) 278 279 replacement = self.resolveIdentifierConflict( 280 self, identifier, self._dict[identifier.name], object 281 ) 282 self._dict[identifier.name] = replacement 283 return 284 285 assert object 286 287 self._dict[identifier.name] = object 288 289 def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): 290 if ( 291 isinstance(originalObject, IDLExternalInterface) 292 and isinstance(newObject, IDLExternalInterface) 293 and originalObject.identifier.name == newObject.identifier.name 294 ): 295 return originalObject 296 297 if isinstance(originalObject, IDLExternalInterface) or isinstance( 298 newObject, IDLExternalInterface 299 ): 300 raise WebIDLError( 301 "Name collision between " 302 "interface declarations for identifier '%s' at '%s' and '%s'" 303 % (identifier.name, originalObject.location, newObject.location), 304 [], 305 ) 306 307 if isinstance(originalObject, IDLDictionary) or isinstance( 308 newObject, IDLDictionary 309 ): 310 raise WebIDLError( 311 "Name collision between dictionary declarations for " 312 "identifier '%s'.\n%s\n%s" 313 % (identifier.name, originalObject.location, newObject.location), 314 [], 315 ) 316 317 # We do the merging of overloads here as opposed to in IDLInterface 318 # because we need to merge overloads of LegacyFactoryFunctions and we need to 319 # detect conflicts in those across interfaces. See also the comment in 320 # IDLInterface.addExtendedAttributes for "LegacyFactoryFunction". 321 if isinstance(originalObject, IDLMethod) and isinstance(newObject, IDLMethod): 322 return originalObject.addOverload(newObject) 323 324 # Default to throwing, derived classes can override. 325 conflictdesc = "\n\t%s at %s\n\t%s at %s" % ( 326 originalObject, 327 originalObject.location, 328 newObject, 329 newObject.location, 330 ) 331 332 raise WebIDLError( 333 "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s" 334 % (identifier.name, str(self), conflictdesc), 335 [], 336 ) 337 338 def _lookupIdentifier(self, identifier): 339 return self._dict[identifier.name] 340 341 def lookupIdentifier(self, identifier): 342 assert isinstance(identifier, IDLIdentifier) 343 assert identifier.scope == self 344 return self._lookupIdentifier(identifier) 345 346 def addIfaceGlobalNames(self, interfaceName, globalNames): 347 """Record the global names (from |globalNames|) that can be used in 348 [Exposed] to expose things in a global named |interfaceName|""" 349 self.globalNames.update(globalNames) 350 for name in globalNames: 351 self.globalNameMapping[name].add(interfaceName) 352 353 354class IDLIdentifier(IDLObject): 355 def __init__(self, location, scope, name): 356 IDLObject.__init__(self, location) 357 358 self.name = name 359 assert isinstance(scope, IDLScope) 360 self.scope = scope 361 362 def __str__(self): 363 return self.QName() 364 365 def QName(self): 366 return self.scope.QName() + self.name 367 368 def __hash__(self): 369 return self.QName().__hash__() 370 371 def __eq__(self, other): 372 return self.QName() == other.QName() 373 374 def object(self): 375 return self.scope.lookupIdentifier(self) 376 377 378class IDLUnresolvedIdentifier(IDLObject): 379 def __init__( 380 self, location, name, allowDoubleUnderscore=False, allowForbidden=False 381 ): 382 IDLObject.__init__(self, location) 383 384 assert len(name) > 0 385 386 if name == "__noSuchMethod__": 387 raise WebIDLError("__noSuchMethod__ is deprecated", [location]) 388 389 if name[:2] == "__" and not allowDoubleUnderscore: 390 raise WebIDLError("Identifiers beginning with __ are reserved", [location]) 391 if name[0] == "_" and not allowDoubleUnderscore: 392 name = name[1:] 393 if name in ["constructor", "toString"] and not allowForbidden: 394 raise WebIDLError( 395 "Cannot use reserved identifier '%s'" % (name), [location] 396 ) 397 398 self.name = name 399 400 def __str__(self): 401 return self.QName() 402 403 def QName(self): 404 return "<unresolved scope>::" + self.name 405 406 def resolve(self, scope, object): 407 assert isinstance(scope, IDLScope) 408 assert not object or isinstance(object, IDLObjectWithIdentifier) 409 assert not object or object.identifier == self 410 411 scope.ensureUnique(self, object) 412 413 identifier = IDLIdentifier(self.location, scope, self.name) 414 if object: 415 object.identifier = identifier 416 return identifier 417 418 def finish(self): 419 assert False # Should replace with a resolved identifier first. 420 421 422class IDLObjectWithIdentifier(IDLObject): 423 def __init__(self, location, parentScope, identifier): 424 IDLObject.__init__(self, location) 425 426 assert isinstance(identifier, IDLUnresolvedIdentifier) 427 428 self.identifier = identifier 429 430 if parentScope: 431 self.resolve(parentScope) 432 433 def resolve(self, parentScope): 434 assert isinstance(parentScope, IDLScope) 435 assert isinstance(self.identifier, IDLUnresolvedIdentifier) 436 self.identifier.resolve(parentScope, self) 437 438 439class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope): 440 def __init__(self, location, parentScope, identifier): 441 assert isinstance(identifier, IDLUnresolvedIdentifier) 442 443 IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) 444 IDLScope.__init__(self, location, parentScope, self.identifier) 445 446 447class IDLIdentifierPlaceholder(IDLObjectWithIdentifier): 448 def __init__(self, location, identifier): 449 assert isinstance(identifier, IDLUnresolvedIdentifier) 450 IDLObjectWithIdentifier.__init__(self, location, None, identifier) 451 452 def finish(self, scope): 453 try: 454 scope._lookupIdentifier(self.identifier) 455 except: 456 raise WebIDLError( 457 "Unresolved type '%s'." % self.identifier, [self.location] 458 ) 459 460 obj = self.identifier.resolve(scope, None) 461 return scope.lookupIdentifier(obj) 462 463 464class IDLExposureMixins: 465 def __init__(self, location): 466 # _exposureGlobalNames are the global names listed in our [Exposed] 467 # extended attribute. exposureSet is the exposure set as defined in the 468 # Web IDL spec: it contains interface names. 469 self._exposureGlobalNames = set() 470 self.exposureSet = set() 471 self._location = location 472 self._globalScope = None 473 474 def finish(self, scope): 475 assert scope.parentScope is None 476 self._globalScope = scope 477 478 # Verify that our [Exposed] value, if any, makes sense. 479 for globalName in self._exposureGlobalNames: 480 if globalName not in scope.globalNames: 481 raise WebIDLError( 482 "Unknown [Exposed] value %s" % globalName, [self._location] 483 ) 484 485 # Verify that we are exposed _somwhere_ if we have some place to be 486 # exposed. We don't want to assert that we're definitely exposed 487 # because a lot of our parser tests have small-enough IDL snippets that 488 # they don't include any globals, and we don't really want to go through 489 # and add global interfaces and [Exposed] annotations to all those 490 # tests. 491 if len(scope.globalNames) != 0: 492 if len(self._exposureGlobalNames) == 0: 493 raise WebIDLError( 494 ( 495 "'%s' is not exposed anywhere even though we have " 496 "globals to be exposed to" 497 ) 498 % self, 499 [self.location], 500 ) 501 502 globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposureSet) 503 504 def isExposedInWindow(self): 505 return "Window" in self.exposureSet 506 507 def isExposedInAnyWorker(self): 508 return len(self.getWorkerExposureSet()) > 0 509 510 def isExposedInWorkerDebugger(self): 511 return len(self.getWorkerDebuggerExposureSet()) > 0 512 513 def isExposedInAnyWorklet(self): 514 return len(self.getWorkletExposureSet()) > 0 515 516 def isExposedInSomeButNotAllWorkers(self): 517 """ 518 Returns true if the Exposed extended attribute for this interface 519 exposes it in some worker globals but not others. The return value does 520 not depend on whether the interface is exposed in Window or System 521 globals. 522 """ 523 if not self.isExposedInAnyWorker(): 524 return False 525 workerScopes = self.parentScope.globalNameMapping["Worker"] 526 return len(workerScopes.difference(self.exposureSet)) > 0 527 528 def getWorkerExposureSet(self): 529 workerScopes = self._globalScope.globalNameMapping["Worker"] 530 return workerScopes.intersection(self.exposureSet) 531 532 def getWorkletExposureSet(self): 533 workletScopes = self._globalScope.globalNameMapping["Worklet"] 534 return workletScopes.intersection(self.exposureSet) 535 536 def getWorkerDebuggerExposureSet(self): 537 workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"] 538 return workerDebuggerScopes.intersection(self.exposureSet) 539 540 541class IDLExternalInterface(IDLObjectWithIdentifier): 542 def __init__(self, location, parentScope, identifier): 543 assert isinstance(identifier, IDLUnresolvedIdentifier) 544 assert isinstance(parentScope, IDLScope) 545 self.parent = None 546 IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) 547 IDLObjectWithIdentifier.resolve(self, parentScope) 548 549 def finish(self, scope): 550 pass 551 552 def validate(self): 553 pass 554 555 def isIteratorInterface(self): 556 return False 557 558 def isExternal(self): 559 return True 560 561 def isInterface(self): 562 return True 563 564 def addExtendedAttributes(self, attrs): 565 if len(attrs) != 0: 566 raise WebIDLError( 567 "There are no extended attributes that are " 568 "allowed on external interfaces", 569 [attrs[0].location, self.location], 570 ) 571 572 def resolve(self, parentScope): 573 pass 574 575 def getJSImplementation(self): 576 return None 577 578 def isJSImplemented(self): 579 return False 580 581 def hasProbablyShortLivingWrapper(self): 582 return False 583 584 def _getDependentObjects(self): 585 return set() 586 587 588class IDLPartialDictionary(IDLObject): 589 def __init__(self, location, name, members, nonPartialDictionary): 590 assert isinstance(name, IDLUnresolvedIdentifier) 591 592 IDLObject.__init__(self, location) 593 self.identifier = name 594 self.members = members 595 self._nonPartialDictionary = nonPartialDictionary 596 self._finished = False 597 nonPartialDictionary.addPartialDictionary(self) 598 599 def addExtendedAttributes(self, attrs): 600 pass 601 602 def finish(self, scope): 603 if self._finished: 604 return 605 self._finished = True 606 607 # Need to make sure our non-partial dictionary gets 608 # finished so it can report cases when we only have partial 609 # dictionaries. 610 self._nonPartialDictionary.finish(scope) 611 612 def validate(self): 613 pass 614 615 616class IDLPartialInterfaceOrNamespace(IDLObject): 617 def __init__(self, location, name, members, nonPartialInterfaceOrNamespace): 618 assert isinstance(name, IDLUnresolvedIdentifier) 619 620 IDLObject.__init__(self, location) 621 self.identifier = name 622 self.members = members 623 # propagatedExtendedAttrs are the ones that should get 624 # propagated to our non-partial interface. 625 self.propagatedExtendedAttrs = [] 626 self._haveSecureContextExtendedAttribute = False 627 self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace 628 self._finished = False 629 nonPartialInterfaceOrNamespace.addPartial(self) 630 631 def addExtendedAttributes(self, attrs): 632 for attr in attrs: 633 identifier = attr.identifier() 634 635 if identifier == "LegacyFactoryFunction": 636 self.propagatedExtendedAttrs.append(attr) 637 elif identifier == "SecureContext": 638 self._haveSecureContextExtendedAttribute = True 639 # This gets propagated to all our members. 640 for member in self.members: 641 if member.getExtendedAttribute("SecureContext"): 642 raise WebIDLError( 643 "[SecureContext] specified on both a " 644 "partial interface member and on the " 645 "partial interface itself", 646 [member.location, attr.location], 647 ) 648 member.addExtendedAttributes([attr]) 649 elif identifier == "Exposed": 650 # This just gets propagated to all our members. 651 for member in self.members: 652 if len(member._exposureGlobalNames) != 0: 653 raise WebIDLError( 654 "[Exposed] specified on both a " 655 "partial interface member and on the " 656 "partial interface itself", 657 [member.location, attr.location], 658 ) 659 member.addExtendedAttributes([attr]) 660 else: 661 raise WebIDLError( 662 "Unknown extended attribute %s on partial " 663 "interface" % identifier, 664 [attr.location], 665 ) 666 667 def finish(self, scope): 668 if self._finished: 669 return 670 self._finished = True 671 if ( 672 not self._haveSecureContextExtendedAttribute 673 and self._nonPartialInterfaceOrNamespace.getExtendedAttribute( 674 "SecureContext" 675 ) 676 ): 677 # This gets propagated to all our members. 678 for member in self.members: 679 if member.getExtendedAttribute("SecureContext"): 680 raise WebIDLError( 681 "[SecureContext] specified on both a " 682 "partial interface member and on the " 683 "non-partial interface", 684 [ 685 member.location, 686 self._nonPartialInterfaceOrNamespace.location, 687 ], 688 ) 689 member.addExtendedAttributes( 690 [ 691 IDLExtendedAttribute( 692 self._nonPartialInterfaceOrNamespace.location, 693 ("SecureContext",), 694 ) 695 ] 696 ) 697 # Need to make sure our non-partial interface or namespace gets 698 # finished so it can report cases when we only have partial 699 # interfaces/namespaces. 700 self._nonPartialInterfaceOrNamespace.finish(scope) 701 702 def validate(self): 703 pass 704 705 706def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet): 707 assert len(targetSet) == 0 708 if exposedAttr.hasValue(): 709 targetSet.add(exposedAttr.value()) 710 else: 711 assert exposedAttr.hasArgs() 712 targetSet.update(exposedAttr.args()) 713 714 715def globalNameSetToExposureSet(globalScope, nameSet, exposureSet): 716 for name in nameSet: 717 exposureSet.update(globalScope.globalNameMapping[name]) 718 719 720class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins): 721 def __init__(self, location, parentScope, name): 722 assert isinstance(parentScope, IDLScope) 723 assert isinstance(name, IDLUnresolvedIdentifier) 724 725 self._finished = False 726 self.members = [] 727 self._partials = [] 728 self._extendedAttrDict = {} 729 self._isKnownNonPartial = False 730 731 IDLObjectWithScope.__init__(self, location, parentScope, name) 732 IDLExposureMixins.__init__(self, location) 733 734 def finish(self, scope): 735 if not self._isKnownNonPartial: 736 raise WebIDLError( 737 "%s does not have a non-partial declaration" % str(self), 738 [self.location], 739 ) 740 741 IDLExposureMixins.finish(self, scope) 742 743 # Now go ahead and merge in our partials. 744 for partial in self._partials: 745 partial.finish(scope) 746 self.addExtendedAttributes(partial.propagatedExtendedAttrs) 747 self.members.extend(partial.members) 748 749 def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): 750 assert isinstance(scope, IDLScope) 751 assert isinstance(originalObject, IDLInterfaceMember) 752 assert isinstance(newObject, IDLInterfaceMember) 753 754 retval = IDLScope.resolveIdentifierConflict( 755 self, scope, identifier, originalObject, newObject 756 ) 757 758 # Might be a ctor, which isn't in self.members 759 if newObject in self.members: 760 self.members.remove(newObject) 761 return retval 762 763 def typeName(self): 764 if self.isInterface(): 765 return "interface" 766 if self.isNamespace(): 767 return "namespace" 768 assert self.isInterfaceMixin() 769 return "interface mixin" 770 771 def getExtendedAttribute(self, name): 772 return self._extendedAttrDict.get(name, None) 773 774 def setNonPartial(self, location, members): 775 if self._isKnownNonPartial: 776 raise WebIDLError( 777 "Two non-partial definitions for the " "same %s" % self.typeName(), 778 [location, self.location], 779 ) 780 self._isKnownNonPartial = True 781 # Now make it look like we were parsed at this new location, since 782 # that's the place where the interface is "really" defined 783 self.location = location 784 # Put the new members at the beginning 785 self.members = members + self.members 786 787 def addPartial(self, partial): 788 assert self.identifier.name == partial.identifier.name 789 self._partials.append(partial) 790 791 def getPartials(self): 792 # Don't let people mutate our guts. 793 return list(self._partials) 794 795 def finishMembers(self, scope): 796 # Assuming we've merged in our partials, set the _exposureGlobalNames on 797 # any members that don't have it set yet. Note that any partial 798 # interfaces that had [Exposed] set have already set up 799 # _exposureGlobalNames on all the members coming from them, so this is 800 # just implementing the "members default to interface or interface mixin 801 # that defined them" and "partial interfaces or interface mixins default 802 # to interface or interface mixin they're a partial for" rules from the 803 # spec. 804 for m in self.members: 805 # If m, or the partial m came from, had [Exposed] 806 # specified, it already has a nonempty exposure global names set. 807 if len(m._exposureGlobalNames) == 0: 808 m._exposureGlobalNames.update(self._exposureGlobalNames) 809 if m.isAttr() and m.stringifier: 810 m.expand(self.members) 811 812 # resolve() will modify self.members, so we need to iterate 813 # over a copy of the member list here. 814 for member in list(self.members): 815 member.resolve(self) 816 817 for member in self.members: 818 member.finish(scope) 819 820 # Now that we've finished our members, which has updated their exposure 821 # sets, make sure they aren't exposed in places where we are not. 822 for member in self.members: 823 if not member.exposureSet.issubset(self.exposureSet): 824 raise WebIDLError( 825 "Interface or interface mixin member has " 826 "larger exposure set than its container", 827 [member.location, self.location], 828 ) 829 830 def isExternal(self): 831 return False 832 833 834class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace): 835 def __init__(self, location, parentScope, name, members, isKnownNonPartial): 836 self.actualExposureGlobalNames = set() 837 838 assert isKnownNonPartial or len(members) == 0 839 IDLInterfaceOrInterfaceMixinOrNamespace.__init__( 840 self, location, parentScope, name 841 ) 842 843 if isKnownNonPartial: 844 self.setNonPartial(location, members) 845 846 def __str__(self): 847 return "Interface mixin '%s'" % self.identifier.name 848 849 def isInterfaceMixin(self): 850 return True 851 852 def finish(self, scope): 853 if self._finished: 854 return 855 self._finished = True 856 857 # Expose to the globals of interfaces that includes this mixin if this 858 # mixin has no explicit [Exposed] so that its members can be exposed 859 # based on the base interface exposure set. 860 # 861 # Make sure this is done before IDLExposureMixins.finish call, since 862 # that converts our set of exposure global names to an actual exposure 863 # set. 864 hasImplicitExposure = len(self._exposureGlobalNames) == 0 865 if hasImplicitExposure: 866 self._exposureGlobalNames.update(self.actualExposureGlobalNames) 867 868 IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) 869 870 self.finishMembers(scope) 871 872 def validate(self): 873 for member in self.members: 874 875 if member.isAttr(): 876 if member.inherit: 877 raise WebIDLError( 878 "Interface mixin member cannot include " 879 "an inherited attribute", 880 [member.location, self.location], 881 ) 882 if member.isStatic(): 883 raise WebIDLError( 884 "Interface mixin member cannot include " "a static member", 885 [member.location, self.location], 886 ) 887 888 if member.isMethod(): 889 if member.isStatic(): 890 raise WebIDLError( 891 "Interface mixin member cannot include " "a static operation", 892 [member.location, self.location], 893 ) 894 if ( 895 member.isGetter() 896 or member.isSetter() 897 or member.isDeleter() 898 or member.isLegacycaller() 899 ): 900 raise WebIDLError( 901 "Interface mixin member cannot include a " "special operation", 902 [member.location, self.location], 903 ) 904 905 def addExtendedAttributes(self, attrs): 906 for attr in attrs: 907 identifier = attr.identifier() 908 909 if identifier == "SecureContext": 910 if not attr.noArguments(): 911 raise WebIDLError( 912 "[%s] must take no arguments" % identifier, [attr.location] 913 ) 914 # This gets propagated to all our members. 915 for member in self.members: 916 if member.getExtendedAttribute("SecureContext"): 917 raise WebIDLError( 918 "[SecureContext] specified on both " 919 "an interface mixin member and on" 920 "the interface mixin itself", 921 [member.location, attr.location], 922 ) 923 member.addExtendedAttributes([attr]) 924 elif identifier == "Exposed": 925 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 926 else: 927 raise WebIDLError( 928 "Unknown extended attribute %s on interface" % identifier, 929 [attr.location], 930 ) 931 932 attrlist = attr.listValue() 933 self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True 934 935 def _getDependentObjects(self): 936 return set(self.members) 937 938 939class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace): 940 def __init__(self, location, parentScope, name, parent, members, isKnownNonPartial): 941 assert isKnownNonPartial or not parent 942 assert isKnownNonPartial or len(members) == 0 943 944 self.parent = None 945 self._callback = False 946 self.maplikeOrSetlikeOrIterable = None 947 # namedConstructors needs deterministic ordering because bindings code 948 # outputs the constructs in the order that namedConstructors enumerates 949 # them. 950 self.legacyFactoryFunctions = list() 951 self.legacyWindowAliases = [] 952 self.includedMixins = set() 953 # self.interfacesBasedOnSelf is the set of interfaces that inherit from 954 # self, including self itself. 955 # Used for distinguishability checking. 956 self.interfacesBasedOnSelf = set([self]) 957 self._hasChildInterfaces = False 958 self._isOnGlobalProtoChain = False 959 # Tracking of the number of reserved slots we need for our 960 # members and those of ancestor interfaces. 961 self.totalMembersInSlots = 0 962 # Tracking of the number of own own members we have in slots 963 self._ownMembersInSlots = 0 964 # If this is an iterator interface, we need to know what iterable 965 # interface we're iterating for in order to get its nativeType. 966 self.iterableInterface = None 967 # True if we have cross-origin members. 968 self.hasCrossOriginMembers = False 969 # True if some descendant (including ourselves) has cross-origin members 970 self.hasDescendantWithCrossOriginMembers = False 971 972 IDLInterfaceOrInterfaceMixinOrNamespace.__init__( 973 self, location, parentScope, name 974 ) 975 976 if isKnownNonPartial: 977 self.setNonPartial(location, parent, members) 978 979 def ctor(self): 980 identifier = IDLUnresolvedIdentifier( 981 self.location, "constructor", allowForbidden=True 982 ) 983 try: 984 return self._lookupIdentifier(identifier) 985 except: 986 return None 987 988 def isIterable(self): 989 return ( 990 self.maplikeOrSetlikeOrIterable 991 and self.maplikeOrSetlikeOrIterable.isIterable() 992 ) 993 994 def isIteratorInterface(self): 995 return self.iterableInterface is not None 996 997 def getClassName(self): 998 return self.identifier.name 999 1000 def finish(self, scope): 1001 if self._finished: 1002 return 1003 1004 self._finished = True 1005 1006 IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) 1007 1008 if len(self.legacyWindowAliases) > 0: 1009 if not self.hasInterfaceObject(): 1010 raise WebIDLError( 1011 "Interface %s unexpectedly has [LegacyWindowAlias] " 1012 "and [LegacyNoInterfaceObject] together" % self.identifier.name, 1013 [self.location], 1014 ) 1015 if not self.isExposedInWindow(): 1016 raise WebIDLError( 1017 "Interface %s has [LegacyWindowAlias] " 1018 "but not exposed in Window" % self.identifier.name, 1019 [self.location], 1020 ) 1021 1022 # Generate maplike/setlike interface members. Since generated members 1023 # need to be treated like regular interface members, do this before 1024 # things like exposure setting. 1025 for member in self.members: 1026 if member.isMaplikeOrSetlikeOrIterable(): 1027 if self.isJSImplemented(): 1028 raise WebIDLError( 1029 "%s declaration used on " 1030 "interface that is implemented in JS" 1031 % (member.maplikeOrSetlikeOrIterableType), 1032 [member.location], 1033 ) 1034 # Check that we only have one interface declaration (currently 1035 # there can only be one maplike/setlike declaration per 1036 # interface) 1037 if self.maplikeOrSetlikeOrIterable: 1038 raise WebIDLError( 1039 "%s declaration used on " 1040 "interface that already has %s " 1041 "declaration" 1042 % ( 1043 member.maplikeOrSetlikeOrIterableType, 1044 self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType, 1045 ), 1046 [self.maplikeOrSetlikeOrIterable.location, member.location], 1047 ) 1048 self.maplikeOrSetlikeOrIterable = member 1049 # If we've got a maplike or setlike declaration, we'll be building all of 1050 # our required methods in Codegen. Generate members now. 1051 self.maplikeOrSetlikeOrIterable.expand(self.members) 1052 1053 assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder) 1054 parent = self.parent.finish(scope) if self.parent else None 1055 if parent and isinstance(parent, IDLExternalInterface): 1056 raise WebIDLError( 1057 "%s inherits from %s which does not have " 1058 "a definition" % (self.identifier.name, self.parent.identifier.name), 1059 [self.location], 1060 ) 1061 if parent and not isinstance(parent, IDLInterface): 1062 raise WebIDLError( 1063 "%s inherits from %s which is not an interface " 1064 % (self.identifier.name, self.parent.identifier.name), 1065 [self.location, parent.location], 1066 ) 1067 1068 self.parent = parent 1069 1070 assert iter(self.members) 1071 1072 if self.isNamespace(): 1073 assert not self.parent 1074 for m in self.members: 1075 if m.isAttr() or m.isMethod(): 1076 if m.isStatic(): 1077 raise WebIDLError( 1078 "Don't mark things explicitly static " "in namespaces", 1079 [self.location, m.location], 1080 ) 1081 # Just mark all our methods/attributes as static. The other 1082 # option is to duplicate the relevant InterfaceMembers 1083 # production bits but modified to produce static stuff to 1084 # start with, but that sounds annoying. 1085 m.forceStatic() 1086 1087 if self.parent: 1088 self.parent.finish(scope) 1089 self.parent._hasChildInterfaces = True 1090 1091 self.totalMembersInSlots = self.parent.totalMembersInSlots 1092 1093 # Interfaces with [Global] must not have anything inherit from them 1094 if self.parent.getExtendedAttribute("Global"): 1095 # Note: This is not a self.parent.isOnGlobalProtoChain() check 1096 # because ancestors of a [Global] interface can have other 1097 # descendants. 1098 raise WebIDLError( 1099 "[Global] interface has another interface " "inheriting from it", 1100 [self.location, self.parent.location], 1101 ) 1102 1103 # Make sure that we're not exposed in places where our parent is not 1104 if not self.exposureSet.issubset(self.parent.exposureSet): 1105 raise WebIDLError( 1106 "Interface %s is exposed in globals where its " 1107 "parent interface %s is not exposed." 1108 % (self.identifier.name, self.parent.identifier.name), 1109 [self.location, self.parent.location], 1110 ) 1111 1112 # Callbacks must not inherit from non-callbacks. 1113 # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending. 1114 if self.isCallback(): 1115 if not self.parent.isCallback(): 1116 raise WebIDLError( 1117 "Callback interface %s inheriting from " 1118 "non-callback interface %s" 1119 % (self.identifier.name, self.parent.identifier.name), 1120 [self.location, self.parent.location], 1121 ) 1122 elif self.parent.isCallback(): 1123 raise WebIDLError( 1124 "Non-callback interface %s inheriting from " 1125 "callback interface %s" 1126 % (self.identifier.name, self.parent.identifier.name), 1127 [self.location, self.parent.location], 1128 ) 1129 1130 # Interfaces which have interface objects can't inherit 1131 # from [LegacyNoInterfaceObject] interfaces. 1132 if self.parent.getExtendedAttribute( 1133 "LegacyNoInterfaceObject" 1134 ) and not self.getExtendedAttribute("LegacyNoInterfaceObject"): 1135 raise WebIDLError( 1136 "Interface %s does not have " 1137 "[LegacyNoInterfaceObject] but inherits from " 1138 "interface %s which does" 1139 % (self.identifier.name, self.parent.identifier.name), 1140 [self.location, self.parent.location], 1141 ) 1142 1143 # Interfaces that are not [SecureContext] can't inherit 1144 # from [SecureContext] interfaces. 1145 if self.parent.getExtendedAttribute( 1146 "SecureContext" 1147 ) and not self.getExtendedAttribute("SecureContext"): 1148 raise WebIDLError( 1149 "Interface %s does not have " 1150 "[SecureContext] but inherits from " 1151 "interface %s which does" 1152 % (self.identifier.name, self.parent.identifier.name), 1153 [self.location, self.parent.location], 1154 ) 1155 1156 for mixin in self.includedMixins: 1157 mixin.finish(scope) 1158 1159 cycleInGraph = self.findInterfaceLoopPoint(self) 1160 if cycleInGraph: 1161 raise WebIDLError( 1162 "Interface %s has itself as ancestor" % self.identifier.name, 1163 [self.location, cycleInGraph.location], 1164 ) 1165 1166 self.finishMembers(scope) 1167 1168 ctor = self.ctor() 1169 if ctor is not None: 1170 if not self.hasInterfaceObject(): 1171 raise WebIDLError( 1172 "Can't have both a constructor and [LegacyNoInterfaceObject]", 1173 [self.location, ctor.location], 1174 ) 1175 1176 if self.globalNames: 1177 raise WebIDLError( 1178 "Can't have both a constructor and [Global]", 1179 [self.location, ctor.location], 1180 ) 1181 1182 assert ctor._exposureGlobalNames == self._exposureGlobalNames 1183 ctor._exposureGlobalNames.update(self._exposureGlobalNames) 1184 # Remove the constructor operation from our member list so 1185 # it doesn't get in the way later. 1186 self.members.remove(ctor) 1187 1188 for ctor in self.legacyFactoryFunctions: 1189 if self.globalNames: 1190 raise WebIDLError( 1191 "Can't have both a legacy factory function and [Global]", 1192 [self.location, ctor.location], 1193 ) 1194 assert len(ctor._exposureGlobalNames) == 0 1195 ctor._exposureGlobalNames.update(self._exposureGlobalNames) 1196 ctor.finish(scope) 1197 1198 # Make a copy of our member list, so things that implement us 1199 # can get those without all the stuff we implement ourselves 1200 # admixed. 1201 self.originalMembers = list(self.members) 1202 1203 for mixin in sorted(self.includedMixins, key=lambda x: x.identifier.name): 1204 for mixinMember in mixin.members: 1205 for member in self.members: 1206 if mixinMember.identifier.name == member.identifier.name: 1207 raise WebIDLError( 1208 "Multiple definitions of %s on %s coming from 'includes' statements" 1209 % (member.identifier.name, self), 1210 [mixinMember.location, member.location], 1211 ) 1212 self.members.extend(mixin.members) 1213 1214 for ancestor in self.getInheritedInterfaces(): 1215 ancestor.interfacesBasedOnSelf.add(self) 1216 if ( 1217 ancestor.maplikeOrSetlikeOrIterable is not None 1218 and self.maplikeOrSetlikeOrIterable is not None 1219 ): 1220 raise WebIDLError( 1221 "Cannot have maplike/setlike on %s that " 1222 "inherits %s, which is already " 1223 "maplike/setlike" 1224 % (self.identifier.name, ancestor.identifier.name), 1225 [ 1226 self.maplikeOrSetlikeOrIterable.location, 1227 ancestor.maplikeOrSetlikeOrIterable.location, 1228 ], 1229 ) 1230 1231 # Deal with interfaces marked [LegacyUnforgeable], now that we have our full 1232 # member list, except unforgeables pulled in from parents. We want to 1233 # do this before we set "originatingInterface" on our unforgeable 1234 # members. 1235 if self.getExtendedAttribute("LegacyUnforgeable"): 1236 # Check that the interface already has all the things the 1237 # spec would otherwise require us to synthesize and is 1238 # missing the ones we plan to synthesize. 1239 if not any(m.isMethod() and m.isStringifier() for m in self.members): 1240 raise WebIDLError( 1241 "LegacyUnforgeable interface %s does not have a " 1242 "stringifier" % self.identifier.name, 1243 [self.location], 1244 ) 1245 1246 for m in self.members: 1247 if m.identifier.name == "toJSON": 1248 raise WebIDLError( 1249 "LegacyUnforgeable interface %s has a " 1250 "toJSON so we won't be able to add " 1251 "one ourselves" % self.identifier.name, 1252 [self.location, m.location], 1253 ) 1254 1255 if m.identifier.name == "valueOf" and not m.isStatic(): 1256 raise WebIDLError( 1257 "LegacyUnforgeable interface %s has a valueOf " 1258 "member so we won't be able to add one " 1259 "ourselves" % self.identifier.name, 1260 [self.location, m.location], 1261 ) 1262 1263 for member in self.members: 1264 if ( 1265 (member.isAttr() or member.isMethod()) 1266 and member.isLegacyUnforgeable() 1267 and not hasattr(member, "originatingInterface") 1268 ): 1269 member.originatingInterface = self 1270 1271 for member in self.members: 1272 if ( 1273 member.isMethod() and member.getExtendedAttribute("CrossOriginCallable") 1274 ) or ( 1275 member.isAttr() 1276 and ( 1277 member.getExtendedAttribute("CrossOriginReadable") 1278 or member.getExtendedAttribute("CrossOriginWritable") 1279 ) 1280 ): 1281 self.hasCrossOriginMembers = True 1282 break 1283 1284 if self.hasCrossOriginMembers: 1285 parent = self 1286 while parent: 1287 parent.hasDescendantWithCrossOriginMembers = True 1288 parent = parent.parent 1289 1290 # Compute slot indices for our members before we pull in unforgeable 1291 # members from our parent. Also, maplike/setlike declarations get a 1292 # slot to hold their backing object. 1293 for member in self.members: 1294 if ( 1295 member.isAttr() 1296 and ( 1297 member.getExtendedAttribute("StoreInSlot") 1298 or member.getExtendedAttribute("Cached") 1299 ) 1300 ) or member.isMaplikeOrSetlike(): 1301 if self.isJSImplemented() and not member.isMaplikeOrSetlike(): 1302 raise WebIDLError( 1303 "Interface %s is JS-implemented and we " 1304 "don't support [Cached] or [StoreInSlot] " 1305 "on JS-implemented interfaces" % self.identifier.name, 1306 [self.location, member.location], 1307 ) 1308 if member.slotIndices is None: 1309 member.slotIndices = dict() 1310 member.slotIndices[self.identifier.name] = self.totalMembersInSlots 1311 self.totalMembersInSlots += 1 1312 if member.getExtendedAttribute("StoreInSlot"): 1313 self._ownMembersInSlots += 1 1314 1315 if self.parent: 1316 # Make sure we don't shadow any of the [LegacyUnforgeable] attributes on our 1317 # ancestor interfaces. We don't have to worry about mixins here, because 1318 # those have already been imported into the relevant .members lists. And 1319 # we don't have to worry about anything other than our parent, because it 1320 # has already imported its ancestors' unforgeable attributes into its 1321 # member list. 1322 for unforgeableMember in ( 1323 member 1324 for member in self.parent.members 1325 if (member.isAttr() or member.isMethod()) 1326 and member.isLegacyUnforgeable() 1327 ): 1328 shadows = [ 1329 m 1330 for m in self.members 1331 if (m.isAttr() or m.isMethod()) 1332 and not m.isStatic() 1333 and m.identifier.name == unforgeableMember.identifier.name 1334 ] 1335 if len(shadows) != 0: 1336 locs = [unforgeableMember.location] + [s.location for s in shadows] 1337 raise WebIDLError( 1338 "Interface %s shadows [LegacyUnforgeable] " 1339 "members of %s" 1340 % (self.identifier.name, ancestor.identifier.name), 1341 locs, 1342 ) 1343 # And now just stick it in our members, since we won't be 1344 # inheriting this down the proto chain. If we really cared we 1345 # could try to do something where we set up the unforgeable 1346 # attributes/methods of ancestor interfaces, with their 1347 # corresponding getters, on our interface, but that gets pretty 1348 # complicated and seems unnecessary. 1349 self.members.append(unforgeableMember) 1350 1351 # At this point, we have all of our members. If the current interface 1352 # uses maplike/setlike, check for collisions anywhere in the current 1353 # interface or higher in the inheritance chain. 1354 if self.maplikeOrSetlikeOrIterable: 1355 testInterface = self 1356 isAncestor = False 1357 while testInterface: 1358 self.maplikeOrSetlikeOrIterable.checkCollisions( 1359 testInterface.members, isAncestor 1360 ) 1361 isAncestor = True 1362 testInterface = testInterface.parent 1363 1364 # Ensure that there's at most one of each {named,indexed} 1365 # {getter,setter,deleter}, at most one stringifier, 1366 # and at most one legacycaller. Note that this last is not 1367 # quite per spec, but in practice no one overloads 1368 # legacycallers. Also note that in practice we disallow 1369 # indexed deleters, but it simplifies some other code to 1370 # treat deleter analogously to getter/setter by 1371 # prefixing it with "named". 1372 specialMembersSeen = {} 1373 for member in self.members: 1374 if not member.isMethod(): 1375 continue 1376 1377 if member.isGetter(): 1378 memberType = "getters" 1379 elif member.isSetter(): 1380 memberType = "setters" 1381 elif member.isDeleter(): 1382 memberType = "deleters" 1383 elif member.isStringifier(): 1384 memberType = "stringifiers" 1385 elif member.isLegacycaller(): 1386 memberType = "legacycallers" 1387 else: 1388 continue 1389 1390 if memberType != "stringifiers" and memberType != "legacycallers": 1391 if member.isNamed(): 1392 memberType = "named " + memberType 1393 else: 1394 assert member.isIndexed() 1395 memberType = "indexed " + memberType 1396 1397 if memberType in specialMembersSeen: 1398 raise WebIDLError( 1399 "Multiple " + memberType + " on %s" % (self), 1400 [ 1401 self.location, 1402 specialMembersSeen[memberType].location, 1403 member.location, 1404 ], 1405 ) 1406 1407 specialMembersSeen[memberType] = member 1408 1409 if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 1410 # Check that we have a named getter. 1411 if "named getters" not in specialMembersSeen: 1412 raise WebIDLError( 1413 "Interface with [LegacyUnenumerableNamedProperties] does " 1414 "not have a named getter", 1415 [self.location], 1416 ) 1417 ancestor = self.parent 1418 while ancestor: 1419 if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 1420 raise WebIDLError( 1421 "Interface with [LegacyUnenumerableNamedProperties] " 1422 "inherits from another interface with " 1423 "[LegacyUnenumerableNamedProperties]", 1424 [self.location, ancestor.location], 1425 ) 1426 ancestor = ancestor.parent 1427 1428 if self._isOnGlobalProtoChain: 1429 # Make sure we have no named setters or deleters 1430 for memberType in ["setter", "deleter"]: 1431 memberId = "named " + memberType + "s" 1432 if memberId in specialMembersSeen: 1433 raise WebIDLError( 1434 "Interface with [Global] has a named %s" % memberType, 1435 [self.location, specialMembersSeen[memberId].location], 1436 ) 1437 # Make sure we're not [LegacyOverrideBuiltIns] 1438 if self.getExtendedAttribute("LegacyOverrideBuiltIns"): 1439 raise WebIDLError( 1440 "Interface with [Global] also has " "[LegacyOverrideBuiltIns]", 1441 [self.location], 1442 ) 1443 # Mark all of our ancestors as being on the global's proto chain too 1444 parent = self.parent 1445 while parent: 1446 # Must not inherit from an interface with [LegacyOverrideBuiltIns] 1447 if parent.getExtendedAttribute("LegacyOverrideBuiltIns"): 1448 raise WebIDLError( 1449 "Interface with [Global] inherits from " 1450 "interface with [LegacyOverrideBuiltIns]", 1451 [self.location, parent.location], 1452 ) 1453 parent._isOnGlobalProtoChain = True 1454 parent = parent.parent 1455 1456 def validate(self): 1457 def checkDuplicateNames(member, name, attributeName): 1458 for m in self.members: 1459 if m.identifier.name == name: 1460 raise WebIDLError( 1461 "[%s=%s] has same name as interface member" 1462 % (attributeName, name), 1463 [member.location, m.location], 1464 ) 1465 if m.isMethod() and m != member and name in m.aliases: 1466 raise WebIDLError( 1467 "conflicting [%s=%s] definitions" % (attributeName, name), 1468 [member.location, m.location], 1469 ) 1470 if m.isAttr() and m != member and name in m.bindingAliases: 1471 raise WebIDLError( 1472 "conflicting [%s=%s] definitions" % (attributeName, name), 1473 [member.location, m.location], 1474 ) 1475 1476 # We also don't support inheriting from unforgeable interfaces. 1477 if self.getExtendedAttribute("LegacyUnforgeable") and self.hasChildInterfaces(): 1478 locations = [self.location] + list( 1479 i.location for i in self.interfacesBasedOnSelf if i.parent == self 1480 ) 1481 raise WebIDLError( 1482 "%s is an unforgeable ancestor interface" % self.identifier.name, 1483 locations, 1484 ) 1485 1486 ctor = self.ctor() 1487 if ctor is not None: 1488 ctor.validate() 1489 for namedCtor in self.legacyFactoryFunctions: 1490 namedCtor.validate() 1491 1492 indexedGetter = None 1493 hasLengthAttribute = False 1494 for member in self.members: 1495 member.validate() 1496 1497 if self.isCallback() and member.getExtendedAttribute("Replaceable"): 1498 raise WebIDLError( 1499 "[Replaceable] used on an attribute on " 1500 "interface %s which is a callback interface" % self.identifier.name, 1501 [self.location, member.location], 1502 ) 1503 1504 # Check that PutForwards refers to another attribute and that no 1505 # cycles exist in forwarded assignments. Also check for a 1506 # integer-typed "length" attribute. 1507 if member.isAttr(): 1508 if member.identifier.name == "length" and member.type.isInteger(): 1509 hasLengthAttribute = True 1510 1511 iface = self 1512 attr = member 1513 putForwards = attr.getExtendedAttribute("PutForwards") 1514 if putForwards and self.isCallback(): 1515 raise WebIDLError( 1516 "[PutForwards] used on an attribute " 1517 "on interface %s which is a callback " 1518 "interface" % self.identifier.name, 1519 [self.location, member.location], 1520 ) 1521 1522 while putForwards is not None: 1523 forwardIface = attr.type.unroll().inner 1524 fowardAttr = None 1525 1526 for forwardedMember in forwardIface.members: 1527 if ( 1528 not forwardedMember.isAttr() 1529 or forwardedMember.identifier.name != putForwards[0] 1530 ): 1531 continue 1532 if forwardedMember == member: 1533 raise WebIDLError( 1534 "Cycle detected in forwarded " 1535 "assignments for attribute %s on " 1536 "%s" % (member.identifier.name, self), 1537 [member.location], 1538 ) 1539 fowardAttr = forwardedMember 1540 break 1541 1542 if fowardAttr is None: 1543 raise WebIDLError( 1544 "Attribute %s on %s forwards to " 1545 "missing attribute %s" 1546 % (attr.identifier.name, iface, putForwards), 1547 [attr.location], 1548 ) 1549 1550 iface = forwardIface 1551 attr = fowardAttr 1552 putForwards = attr.getExtendedAttribute("PutForwards") 1553 1554 # Check that the name of an [Alias] doesn't conflict with an 1555 # interface member and whether we support indexed properties. 1556 if member.isMethod(): 1557 if member.isGetter() and member.isIndexed(): 1558 indexedGetter = member 1559 1560 for alias in member.aliases: 1561 if self.isOnGlobalProtoChain(): 1562 raise WebIDLError( 1563 "[Alias] must not be used on a " 1564 "[Global] interface operation", 1565 [member.location], 1566 ) 1567 if ( 1568 member.getExtendedAttribute("Exposed") 1569 or member.getExtendedAttribute("ChromeOnly") 1570 or member.getExtendedAttribute("Pref") 1571 or member.getExtendedAttribute("Func") 1572 or member.getExtendedAttribute("SecureContext") 1573 ): 1574 raise WebIDLError( 1575 "[Alias] must not be used on a " 1576 "conditionally exposed operation", 1577 [member.location], 1578 ) 1579 if member.isStatic(): 1580 raise WebIDLError( 1581 "[Alias] must not be used on a " "static operation", 1582 [member.location], 1583 ) 1584 if member.isIdentifierLess(): 1585 raise WebIDLError( 1586 "[Alias] must not be used on an " 1587 "identifierless operation", 1588 [member.location], 1589 ) 1590 if member.isLegacyUnforgeable(): 1591 raise WebIDLError( 1592 "[Alias] must not be used on an " 1593 "[LegacyUnforgeable] operation", 1594 [member.location], 1595 ) 1596 1597 checkDuplicateNames(member, alias, "Alias") 1598 1599 # Check that the name of a [BindingAlias] doesn't conflict with an 1600 # interface member. 1601 if member.isAttr(): 1602 for bindingAlias in member.bindingAliases: 1603 checkDuplicateNames(member, bindingAlias, "BindingAlias") 1604 1605 # Conditional exposure makes no sense for interfaces with no 1606 # interface object. 1607 # And SecureContext makes sense for interfaces with no interface object, 1608 # since it is also propagated to interface members. 1609 if ( 1610 self.isExposedConditionally(exclusions=["SecureContext"]) 1611 and not self.hasInterfaceObject() 1612 ): 1613 raise WebIDLError( 1614 "Interface with no interface object is " "exposed conditionally", 1615 [self.location], 1616 ) 1617 1618 # Value iterators are only allowed on interfaces with indexed getters, 1619 # and pair iterators are only allowed on interfaces without indexed 1620 # getters. 1621 if self.isIterable(): 1622 iterableDecl = self.maplikeOrSetlikeOrIterable 1623 if iterableDecl.isValueIterator(): 1624 if not indexedGetter: 1625 raise WebIDLError( 1626 "Interface with value iterator does not " 1627 "support indexed properties", 1628 [self.location, iterableDecl.location], 1629 ) 1630 1631 if iterableDecl.valueType != indexedGetter.signatures()[0][0]: 1632 raise WebIDLError( 1633 "Iterable type does not match indexed " "getter type", 1634 [iterableDecl.location, indexedGetter.location], 1635 ) 1636 1637 if not hasLengthAttribute: 1638 raise WebIDLError( 1639 "Interface with value iterator does not " 1640 'have an integer-typed "length" attribute', 1641 [self.location, iterableDecl.location], 1642 ) 1643 else: 1644 assert iterableDecl.isPairIterator() 1645 if indexedGetter: 1646 raise WebIDLError( 1647 "Interface with pair iterator supports " "indexed properties", 1648 [self.location, iterableDecl.location, indexedGetter.location], 1649 ) 1650 1651 if indexedGetter and not hasLengthAttribute: 1652 raise WebIDLError( 1653 "Interface with an indexed getter does not have " 1654 'an integer-typed "length" attribute', 1655 [self.location, indexedGetter.location], 1656 ) 1657 1658 def setCallback(self, value): 1659 self._callback = value 1660 1661 def isCallback(self): 1662 return self._callback 1663 1664 def isSingleOperationInterface(self): 1665 assert self.isCallback() or self.isJSImplemented() 1666 return ( 1667 # JS-implemented things should never need the 1668 # this-handling weirdness of single-operation interfaces. 1669 not self.isJSImplemented() 1670 and 1671 # Not inheriting from another interface 1672 not self.parent 1673 and 1674 # No attributes of any kinds 1675 not any(m.isAttr() for m in self.members) 1676 and 1677 # There is at least one regular operation, and all regular 1678 # operations have the same identifier 1679 len( 1680 set( 1681 m.identifier.name 1682 for m in self.members 1683 if m.isMethod() and not m.isStatic() 1684 ) 1685 ) 1686 == 1 1687 ) 1688 1689 def inheritanceDepth(self): 1690 depth = 0 1691 parent = self.parent 1692 while parent: 1693 depth = depth + 1 1694 parent = parent.parent 1695 return depth 1696 1697 def hasConstants(self): 1698 return any(m.isConst() for m in self.members) 1699 1700 def hasInterfaceObject(self): 1701 if self.isCallback(): 1702 return self.hasConstants() 1703 return not hasattr(self, "_noInterfaceObject") 1704 1705 def hasInterfacePrototypeObject(self): 1706 return ( 1707 not self.isCallback() 1708 and not self.isNamespace() 1709 and self.getUserData("hasConcreteDescendant", False) 1710 ) 1711 1712 def addIncludedMixin(self, includedMixin): 1713 assert isinstance(includedMixin, IDLInterfaceMixin) 1714 self.includedMixins.add(includedMixin) 1715 1716 def getInheritedInterfaces(self): 1717 """ 1718 Returns a list of the interfaces this interface inherits from 1719 (not including this interface itself). The list is in order 1720 from most derived to least derived. 1721 """ 1722 assert self._finished 1723 if not self.parent: 1724 return [] 1725 parentInterfaces = self.parent.getInheritedInterfaces() 1726 parentInterfaces.insert(0, self.parent) 1727 return parentInterfaces 1728 1729 def findInterfaceLoopPoint(self, otherInterface): 1730 """ 1731 Finds an interface amongst our ancestors that inherits from otherInterface. 1732 If there is no such interface, returns None. 1733 """ 1734 if self.parent: 1735 if self.parent == otherInterface: 1736 return self 1737 loopPoint = self.parent.findInterfaceLoopPoint(otherInterface) 1738 if loopPoint: 1739 return loopPoint 1740 return None 1741 1742 def setNonPartial(self, location, parent, members): 1743 assert not parent or isinstance(parent, IDLIdentifierPlaceholder) 1744 IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members) 1745 assert not self.parent 1746 self.parent = parent 1747 1748 def getJSImplementation(self): 1749 classId = self.getExtendedAttribute("JSImplementation") 1750 if not classId: 1751 return classId 1752 assert isinstance(classId, list) 1753 assert len(classId) == 1 1754 return classId[0] 1755 1756 def isJSImplemented(self): 1757 return bool(self.getJSImplementation()) 1758 1759 def hasProbablyShortLivingWrapper(self): 1760 current = self 1761 while current: 1762 if current.getExtendedAttribute("ProbablyShortLivingWrapper"): 1763 return True 1764 current = current.parent 1765 return False 1766 1767 def hasChildInterfaces(self): 1768 return self._hasChildInterfaces 1769 1770 def isOnGlobalProtoChain(self): 1771 return self._isOnGlobalProtoChain 1772 1773 def _getDependentObjects(self): 1774 deps = set(self.members) 1775 deps.update(self.includedMixins) 1776 if self.parent: 1777 deps.add(self.parent) 1778 return deps 1779 1780 def hasMembersInSlots(self): 1781 return self._ownMembersInSlots != 0 1782 1783 conditionExtendedAttributes = ["Pref", "ChromeOnly", "Func", "SecureContext"] 1784 1785 def isExposedConditionally(self, exclusions=[]): 1786 return any( 1787 ((not a in exclusions) and self.getExtendedAttribute(a)) 1788 for a in self.conditionExtendedAttributes 1789 ) 1790 1791 1792class IDLInterface(IDLInterfaceOrNamespace): 1793 def __init__( 1794 self, 1795 location, 1796 parentScope, 1797 name, 1798 parent, 1799 members, 1800 isKnownNonPartial, 1801 classNameOverride=None, 1802 ): 1803 IDLInterfaceOrNamespace.__init__( 1804 self, location, parentScope, name, parent, members, isKnownNonPartial 1805 ) 1806 self.classNameOverride = classNameOverride 1807 1808 def __str__(self): 1809 return "Interface '%s'" % self.identifier.name 1810 1811 def isInterface(self): 1812 return True 1813 1814 def getClassName(self): 1815 if self.classNameOverride: 1816 return self.classNameOverride 1817 return IDLInterfaceOrNamespace.getClassName(self) 1818 1819 def addExtendedAttributes(self, attrs): 1820 for attr in attrs: 1821 identifier = attr.identifier() 1822 1823 # Special cased attrs 1824 if identifier == "TreatNonCallableAsNull": 1825 raise WebIDLError( 1826 "TreatNonCallableAsNull cannot be specified on interfaces", 1827 [attr.location, self.location], 1828 ) 1829 if identifier == "LegacyTreatNonObjectAsNull": 1830 raise WebIDLError( 1831 "LegacyTreatNonObjectAsNull cannot be specified on interfaces", 1832 [attr.location, self.location], 1833 ) 1834 elif identifier == "LegacyNoInterfaceObject": 1835 if not attr.noArguments(): 1836 raise WebIDLError( 1837 "[LegacyNoInterfaceObject] must take no arguments", 1838 [attr.location], 1839 ) 1840 1841 self._noInterfaceObject = True 1842 elif identifier == "LegacyFactoryFunction": 1843 if not attr.hasValue(): 1844 raise WebIDLError( 1845 "LegacyFactoryFunction must either take an identifier or take a named argument list", 1846 [attr.location], 1847 ) 1848 1849 args = attr.args() if attr.hasArgs() else [] 1850 1851 retType = IDLWrapperType(self.location, self) 1852 1853 method = IDLConstructor(attr.location, args, attr.value()) 1854 method.reallyInit(self) 1855 1856 # Named constructors are always assumed to be able to 1857 # throw (since there's no way to indicate otherwise). 1858 method.addExtendedAttributes( 1859 [IDLExtendedAttribute(self.location, ("Throws",))] 1860 ) 1861 1862 # We need to detect conflicts for LegacyFactoryFunctions across 1863 # interfaces. We first call resolve on the parentScope, 1864 # which will merge all LegacyFactoryFunctions with the same 1865 # identifier accross interfaces as overloads. 1866 method.resolve(self.parentScope) 1867 1868 # Then we look up the identifier on the parentScope. If the 1869 # result is the same as the method we're adding then it 1870 # hasn't been added as an overload and it's the first time 1871 # we've encountered a LegacyFactoryFunction with that identifier. 1872 # If the result is not the same as the method we're adding 1873 # then it has been added as an overload and we need to check 1874 # whether the result is actually one of our existing 1875 # LegacyFactoryFunctions. 1876 newMethod = self.parentScope.lookupIdentifier(method.identifier) 1877 if newMethod == method: 1878 self.legacyFactoryFunctions.append(method) 1879 elif newMethod not in self.legacyFactoryFunctions: 1880 raise WebIDLError( 1881 "LegacyFactoryFunction conflicts with a " 1882 "LegacyFactoryFunction of a different interface", 1883 [method.location, newMethod.location], 1884 ) 1885 elif identifier == "ExceptionClass": 1886 if not attr.noArguments(): 1887 raise WebIDLError( 1888 "[ExceptionClass] must take no arguments", [attr.location] 1889 ) 1890 if self.parent: 1891 raise WebIDLError( 1892 "[ExceptionClass] must not be specified on " 1893 "an interface with inherited interfaces", 1894 [attr.location, self.location], 1895 ) 1896 elif identifier == "Global": 1897 if attr.hasValue(): 1898 self.globalNames = [attr.value()] 1899 elif attr.hasArgs(): 1900 self.globalNames = attr.args() 1901 else: 1902 self.globalNames = [self.identifier.name] 1903 self.parentScope.addIfaceGlobalNames( 1904 self.identifier.name, self.globalNames 1905 ) 1906 self._isOnGlobalProtoChain = True 1907 elif identifier == "LegacyWindowAlias": 1908 if attr.hasValue(): 1909 self.legacyWindowAliases = [attr.value()] 1910 elif attr.hasArgs(): 1911 self.legacyWindowAliases = attr.args() 1912 else: 1913 raise WebIDLError( 1914 "[%s] must either take an identifier " 1915 "or take an identifier list" % identifier, 1916 [attr.location], 1917 ) 1918 for alias in self.legacyWindowAliases: 1919 unresolved = IDLUnresolvedIdentifier(attr.location, alias) 1920 IDLObjectWithIdentifier(attr.location, self.parentScope, unresolved) 1921 elif identifier == "SecureContext": 1922 if not attr.noArguments(): 1923 raise WebIDLError( 1924 "[%s] must take no arguments" % identifier, [attr.location] 1925 ) 1926 # This gets propagated to all our members. 1927 for member in self.members: 1928 if member.getExtendedAttribute("SecureContext"): 1929 raise WebIDLError( 1930 "[SecureContext] specified on both " 1931 "an interface member and on the " 1932 "interface itself", 1933 [member.location, attr.location], 1934 ) 1935 member.addExtendedAttributes([attr]) 1936 elif ( 1937 identifier == "NeedResolve" 1938 or identifier == "LegacyOverrideBuiltIns" 1939 or identifier == "ChromeOnly" 1940 or identifier == "LegacyUnforgeable" 1941 or identifier == "LegacyEventInit" 1942 or identifier == "ProbablyShortLivingWrapper" 1943 or identifier == "LegacyUnenumerableNamedProperties" 1944 or identifier == "RunConstructorInCallerCompartment" 1945 or identifier == "WantsEventListenerHooks" 1946 or identifier == "Serializable" 1947 ): 1948 # Known extended attributes that do not take values 1949 if not attr.noArguments(): 1950 raise WebIDLError( 1951 "[%s] must take no arguments" % identifier, [attr.location] 1952 ) 1953 elif identifier == "Exposed": 1954 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 1955 elif ( 1956 identifier == "Pref" 1957 or identifier == "JSImplementation" 1958 or identifier == "HeaderFile" 1959 or identifier == "Func" 1960 or identifier == "Deprecated" 1961 ): 1962 # Known extended attributes that take a string value 1963 if not attr.hasValue(): 1964 raise WebIDLError( 1965 "[%s] must have a value" % identifier, [attr.location] 1966 ) 1967 elif identifier == "InstrumentedProps": 1968 # Known extended attributes that take a list 1969 if not attr.hasArgs(): 1970 raise WebIDLError( 1971 "[%s] must have arguments" % identifier, [attr.location] 1972 ) 1973 else: 1974 raise WebIDLError( 1975 "Unknown extended attribute %s on interface" % identifier, 1976 [attr.location], 1977 ) 1978 1979 attrlist = attr.listValue() 1980 self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True 1981 1982 def validate(self): 1983 IDLInterfaceOrNamespace.validate(self) 1984 if self.parent and self.isSerializable() and not self.parent.isSerializable(): 1985 raise WebIDLError( 1986 "Serializable interface inherits from non-serializable " 1987 "interface. Per spec, that means the object should not be " 1988 "serializable, so chances are someone made a mistake here " 1989 "somewhere.", 1990 [self.location, self.parent.location], 1991 ) 1992 1993 def isSerializable(self): 1994 return self.getExtendedAttribute("Serializable") 1995 1996 def setNonPartial(self, location, parent, members): 1997 # Before we do anything else, finish initializing any constructors that 1998 # might be in "members", so we don't have partially-initialized objects 1999 # hanging around. We couldn't do it before now because we needed to have 2000 # to have the IDLInterface on hand to properly set the return type. 2001 for member in members: 2002 if isinstance(member, IDLConstructor): 2003 member.reallyInit(self) 2004 2005 IDLInterfaceOrNamespace.setNonPartial(self, location, parent, members) 2006 2007 2008class IDLNamespace(IDLInterfaceOrNamespace): 2009 def __init__(self, location, parentScope, name, members, isKnownNonPartial): 2010 IDLInterfaceOrNamespace.__init__( 2011 self, location, parentScope, name, None, members, isKnownNonPartial 2012 ) 2013 2014 def __str__(self): 2015 return "Namespace '%s'" % self.identifier.name 2016 2017 def isNamespace(self): 2018 return True 2019 2020 def addExtendedAttributes(self, attrs): 2021 # The set of things namespaces support is small enough it's simpler 2022 # to factor out into a separate method than it is to sprinkle 2023 # isNamespace() checks all through 2024 # IDLInterfaceOrNamespace.addExtendedAttributes. 2025 for attr in attrs: 2026 identifier = attr.identifier() 2027 2028 if identifier == "Exposed": 2029 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 2030 elif identifier == "ClassString": 2031 # Takes a string value to override the default "Object" if 2032 # desired. 2033 if not attr.hasValue(): 2034 raise WebIDLError( 2035 "[%s] must have a value" % identifier, [attr.location] 2036 ) 2037 elif identifier == "ProtoObjectHack" or identifier == "ChromeOnly": 2038 if not attr.noArguments(): 2039 raise WebIDLError( 2040 "[%s] must not have arguments" % identifier, [attr.location] 2041 ) 2042 elif ( 2043 identifier == "Pref" 2044 or identifier == "HeaderFile" 2045 or identifier == "Func" 2046 ): 2047 # Known extended attributes that take a string value 2048 if not attr.hasValue(): 2049 raise WebIDLError( 2050 "[%s] must have a value" % identifier, [attr.location] 2051 ) 2052 else: 2053 raise WebIDLError( 2054 "Unknown extended attribute %s on namespace" % identifier, 2055 [attr.location], 2056 ) 2057 2058 attrlist = attr.listValue() 2059 self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True 2060 2061 def isSerializable(self): 2062 return False 2063 2064 2065class IDLDictionary(IDLObjectWithScope): 2066 def __init__(self, location, parentScope, name, parent, members): 2067 assert isinstance(parentScope, IDLScope) 2068 assert isinstance(name, IDLUnresolvedIdentifier) 2069 assert not parent or isinstance(parent, IDLIdentifierPlaceholder) 2070 2071 self.parent = parent 2072 self._finished = False 2073 self.members = list(members) 2074 self._partialDictionaries = [] 2075 self._extendedAttrDict = {} 2076 self.needsConversionToJS = False 2077 self.needsConversionFromJS = False 2078 2079 IDLObjectWithScope.__init__(self, location, parentScope, name) 2080 2081 def __str__(self): 2082 return "Dictionary '%s'" % self.identifier.name 2083 2084 def isDictionary(self): 2085 return True 2086 2087 def canBeEmpty(self): 2088 """ 2089 Returns true if this dictionary can be empty (that is, it has no 2090 required members and neither do any of its ancestors). 2091 """ 2092 return all(member.optional for member in self.members) and ( 2093 not self.parent or self.parent.canBeEmpty() 2094 ) 2095 2096 def finish(self, scope): 2097 if self._finished: 2098 return 2099 2100 self._finished = True 2101 2102 if self.parent: 2103 assert isinstance(self.parent, IDLIdentifierPlaceholder) 2104 oldParent = self.parent 2105 self.parent = self.parent.finish(scope) 2106 if not isinstance(self.parent, IDLDictionary): 2107 raise WebIDLError( 2108 "Dictionary %s has parent that is not a dictionary" 2109 % self.identifier.name, 2110 [oldParent.location, self.parent.location], 2111 ) 2112 2113 # Make sure the parent resolves all its members before we start 2114 # looking at them. 2115 self.parent.finish(scope) 2116 2117 # Now go ahead and merge in our partial dictionaries. 2118 for partial in self._partialDictionaries: 2119 partial.finish(scope) 2120 self.members.extend(partial.members) 2121 2122 for member in self.members: 2123 member.resolve(self) 2124 if not member.isComplete(): 2125 member.complete(scope) 2126 assert member.type.isComplete() 2127 2128 # Members of a dictionary are sorted in lexicographic order 2129 self.members.sort(key=lambda x: x.identifier.name) 2130 2131 inheritedMembers = [] 2132 ancestor = self.parent 2133 while ancestor: 2134 if ancestor == self: 2135 raise WebIDLError( 2136 "Dictionary %s has itself as an ancestor" % self.identifier.name, 2137 [self.identifier.location], 2138 ) 2139 inheritedMembers.extend(ancestor.members) 2140 ancestor = ancestor.parent 2141 2142 # Catch name duplication 2143 for inheritedMember in inheritedMembers: 2144 for member in self.members: 2145 if member.identifier.name == inheritedMember.identifier.name: 2146 raise WebIDLError( 2147 "Dictionary %s has two members with name %s" 2148 % (self.identifier.name, member.identifier.name), 2149 [member.location, inheritedMember.location], 2150 ) 2151 2152 def validate(self): 2153 def typeContainsDictionary(memberType, dictionary): 2154 """ 2155 Returns a tuple whose: 2156 2157 - First element is a Boolean value indicating whether 2158 memberType contains dictionary. 2159 2160 - Second element is: 2161 A list of locations that leads from the type that was passed in 2162 the memberType argument, to the dictionary being validated, 2163 if the boolean value in the first element is True. 2164 2165 None, if the boolean value in the first element is False. 2166 """ 2167 2168 if ( 2169 memberType.nullable() 2170 or memberType.isSequence() 2171 or memberType.isRecord() 2172 ): 2173 return typeContainsDictionary(memberType.inner, dictionary) 2174 2175 if memberType.isDictionary(): 2176 if memberType.inner == dictionary: 2177 return (True, [memberType.location]) 2178 2179 (contains, locations) = dictionaryContainsDictionary( 2180 memberType.inner, dictionary 2181 ) 2182 if contains: 2183 return (True, [memberType.location] + locations) 2184 2185 if memberType.isUnion(): 2186 for member in memberType.flatMemberTypes: 2187 (contains, locations) = typeContainsDictionary(member, dictionary) 2188 if contains: 2189 return (True, locations) 2190 2191 return (False, None) 2192 2193 def dictionaryContainsDictionary(dictMember, dictionary): 2194 for member in dictMember.members: 2195 (contains, locations) = typeContainsDictionary(member.type, dictionary) 2196 if contains: 2197 return (True, [member.location] + locations) 2198 2199 if dictMember.parent: 2200 if dictMember.parent == dictionary: 2201 return (True, [dictMember.location]) 2202 else: 2203 (contains, locations) = dictionaryContainsDictionary( 2204 dictMember.parent, dictionary 2205 ) 2206 if contains: 2207 return (True, [dictMember.location] + locations) 2208 2209 return (False, None) 2210 2211 for member in self.members: 2212 if member.type.isDictionary() and member.type.nullable(): 2213 raise WebIDLError( 2214 "Dictionary %s has member with nullable " 2215 "dictionary type" % self.identifier.name, 2216 [member.location], 2217 ) 2218 (contains, locations) = typeContainsDictionary(member.type, self) 2219 if contains: 2220 raise WebIDLError( 2221 "Dictionary %s has member with itself as type." 2222 % self.identifier.name, 2223 [member.location] + locations, 2224 ) 2225 2226 def getExtendedAttribute(self, name): 2227 return self._extendedAttrDict.get(name, None) 2228 2229 def addExtendedAttributes(self, attrs): 2230 for attr in attrs: 2231 identifier = attr.identifier() 2232 2233 if identifier == "GenerateInitFromJSON" or identifier == "GenerateInit": 2234 if not attr.noArguments(): 2235 raise WebIDLError( 2236 "[%s] must not have arguments" % identifier, [attr.location] 2237 ) 2238 self.needsConversionFromJS = True 2239 elif ( 2240 identifier == "GenerateConversionToJS" or identifier == "GenerateToJSON" 2241 ): 2242 if not attr.noArguments(): 2243 raise WebIDLError( 2244 "[%s] must not have arguments" % identifier, [attr.location] 2245 ) 2246 # ToJSON methods require to-JS conversion, because we 2247 # implement ToJSON by converting to a JS object and 2248 # then using JSON.stringify. 2249 self.needsConversionToJS = True 2250 else: 2251 raise WebIDLError( 2252 "[%s] extended attribute not allowed on " 2253 "dictionaries" % identifier, 2254 [attr.location], 2255 ) 2256 2257 self._extendedAttrDict[identifier] = True 2258 2259 def _getDependentObjects(self): 2260 deps = set(self.members) 2261 if self.parent: 2262 deps.add(self.parent) 2263 return deps 2264 2265 def addPartialDictionary(self, partial): 2266 assert self.identifier.name == partial.identifier.name 2267 self._partialDictionaries.append(partial) 2268 2269 2270class IDLEnum(IDLObjectWithIdentifier): 2271 def __init__(self, location, parentScope, name, values): 2272 assert isinstance(parentScope, IDLScope) 2273 assert isinstance(name, IDLUnresolvedIdentifier) 2274 2275 if len(values) != len(set(values)): 2276 raise WebIDLError( 2277 "Enum %s has multiple identical strings" % name.name, [location] 2278 ) 2279 2280 IDLObjectWithIdentifier.__init__(self, location, parentScope, name) 2281 self._values = values 2282 2283 def values(self): 2284 return self._values 2285 2286 def finish(self, scope): 2287 pass 2288 2289 def validate(self): 2290 pass 2291 2292 def isEnum(self): 2293 return True 2294 2295 def addExtendedAttributes(self, attrs): 2296 if len(attrs) != 0: 2297 raise WebIDLError( 2298 "There are no extended attributes that are " "allowed on enums", 2299 [attrs[0].location, self.location], 2300 ) 2301 2302 def _getDependentObjects(self): 2303 return set() 2304 2305 2306class IDLType(IDLObject): 2307 Tags = enum( 2308 # The integer types 2309 "int8", 2310 "uint8", 2311 "int16", 2312 "uint16", 2313 "int32", 2314 "uint32", 2315 "int64", 2316 "uint64", 2317 # Additional primitive types 2318 "bool", 2319 "unrestricted_float", 2320 "float", 2321 "unrestricted_double", 2322 # "double" last primitive type to match IDLBuiltinType 2323 "double", 2324 # Other types 2325 "any", 2326 "domstring", 2327 "bytestring", 2328 "usvstring", 2329 "utf8string", 2330 "jsstring", 2331 "object", 2332 "void", 2333 # Funny stuff 2334 "interface", 2335 "dictionary", 2336 "enum", 2337 "callback", 2338 "union", 2339 "sequence", 2340 "record", 2341 "promise", 2342 ) 2343 2344 def __init__(self, location, name): 2345 IDLObject.__init__(self, location) 2346 self.name = name 2347 self.builtin = False 2348 self.legacyNullToEmptyString = False 2349 self._clamp = False 2350 self._enforceRange = False 2351 self._allowShared = False 2352 self._extendedAttrDict = {} 2353 2354 def __hash__(self): 2355 return ( 2356 hash(self.builtin) 2357 + hash(self.name) 2358 + hash(self._clamp) 2359 + hash(self._enforceRange) 2360 + hash(self.legacyNullToEmptyString) 2361 + hash(self._allowShared) 2362 ) 2363 2364 def __eq__(self, other): 2365 return ( 2366 other 2367 and self.builtin == other.builtin 2368 and self.name == other.name 2369 and self._clamp == other.hasClamp() 2370 and self._enforceRange == other.hasEnforceRange() 2371 and self.legacyNullToEmptyString == other.legacyNullToEmptyString 2372 and self._allowShared == other.hasAllowShared() 2373 ) 2374 2375 def __ne__(self, other): 2376 return not self == other 2377 2378 def __str__(self): 2379 return str(self.name) 2380 2381 def prettyName(self): 2382 """ 2383 A name that looks like what this type is named in the IDL spec. By default 2384 this is just our .name, but types that have more interesting spec 2385 representations should override this. 2386 """ 2387 return str(self.name) 2388 2389 def isType(self): 2390 return True 2391 2392 def nullable(self): 2393 return False 2394 2395 def isPrimitive(self): 2396 return False 2397 2398 def isBoolean(self): 2399 return False 2400 2401 def isNumeric(self): 2402 return False 2403 2404 def isString(self): 2405 return False 2406 2407 def isByteString(self): 2408 return False 2409 2410 def isDOMString(self): 2411 return False 2412 2413 def isUSVString(self): 2414 return False 2415 2416 def isUTF8String(self): 2417 return False 2418 2419 def isJSString(self): 2420 return False 2421 2422 def isVoid(self): 2423 return self.name == "Void" 2424 2425 def isSequence(self): 2426 return False 2427 2428 def isRecord(self): 2429 return False 2430 2431 def isReadableStream(self): 2432 return False 2433 2434 def isArrayBuffer(self): 2435 return False 2436 2437 def isArrayBufferView(self): 2438 return False 2439 2440 def isTypedArray(self): 2441 return False 2442 2443 def isBufferSource(self): 2444 return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray() 2445 2446 def isCallbackInterface(self): 2447 return False 2448 2449 def isNonCallbackInterface(self): 2450 return False 2451 2452 def isGeckoInterface(self): 2453 """Returns a boolean indicating whether this type is an 'interface' 2454 type that is implemented in Gecko. At the moment, this returns 2455 true for all interface types that are not types from the TypedArray 2456 spec.""" 2457 return self.isInterface() and not self.isSpiderMonkeyInterface() 2458 2459 def isSpiderMonkeyInterface(self): 2460 """Returns a boolean indicating whether this type is an 'interface' 2461 type that is implemented in SpiderMonkey.""" 2462 return self.isInterface() and (self.isBufferSource() or self.isReadableStream()) 2463 2464 def isAny(self): 2465 return self.tag() == IDLType.Tags.any 2466 2467 def isObject(self): 2468 return self.tag() == IDLType.Tags.object 2469 2470 def isPromise(self): 2471 return False 2472 2473 def isComplete(self): 2474 return True 2475 2476 def includesRestrictedFloat(self): 2477 return False 2478 2479 def isFloat(self): 2480 return False 2481 2482 def isUnrestricted(self): 2483 # Should only call this on float types 2484 assert self.isFloat() 2485 2486 def isJSONType(self): 2487 return False 2488 2489 def hasClamp(self): 2490 return self._clamp 2491 2492 def hasEnforceRange(self): 2493 return self._enforceRange 2494 2495 def hasAllowShared(self): 2496 return self._allowShared 2497 2498 def tag(self): 2499 assert False # Override me! 2500 2501 def treatNonCallableAsNull(self): 2502 assert self.tag() == IDLType.Tags.callback 2503 return self.nullable() and self.inner.callback._treatNonCallableAsNull 2504 2505 def treatNonObjectAsNull(self): 2506 assert self.tag() == IDLType.Tags.callback 2507 return self.nullable() and self.inner.callback._treatNonObjectAsNull 2508 2509 def withExtendedAttributes(self, attrs): 2510 if len(attrs) > 0: 2511 raise WebIDLError( 2512 "Extended attributes on types only supported for builtins", 2513 [attrs[0].location, self.location], 2514 ) 2515 return self 2516 2517 def getExtendedAttribute(self, name): 2518 return self._extendedAttrDict.get(name, None) 2519 2520 def resolveType(self, parentScope): 2521 pass 2522 2523 def unroll(self): 2524 return self 2525 2526 def isDistinguishableFrom(self, other): 2527 raise TypeError( 2528 "Can't tell whether a generic type is or is not " 2529 "distinguishable from other things" 2530 ) 2531 2532 def isExposedInAllOf(self, exposureSet): 2533 return True 2534 2535 2536class IDLUnresolvedType(IDLType): 2537 """ 2538 Unresolved types are interface types 2539 """ 2540 2541 def __init__(self, location, name, attrs=[]): 2542 IDLType.__init__(self, location, name) 2543 self.extraTypeAttributes = attrs 2544 2545 def isComplete(self): 2546 return False 2547 2548 def complete(self, scope): 2549 obj = None 2550 try: 2551 obj = scope._lookupIdentifier(self.name) 2552 except: 2553 raise WebIDLError("Unresolved type '%s'." % self.name, [self.location]) 2554 2555 assert obj 2556 if obj.isType(): 2557 print(obj) 2558 assert not obj.isType() 2559 if obj.isTypedef(): 2560 assert self.name.name == obj.identifier.name 2561 typedefType = IDLTypedefType(self.location, obj.innerType, obj.identifier) 2562 assert not typedefType.isComplete() 2563 return typedefType.complete(scope).withExtendedAttributes( 2564 self.extraTypeAttributes 2565 ) 2566 elif obj.isCallback() and not obj.isInterface(): 2567 assert self.name.name == obj.identifier.name 2568 return IDLCallbackType(self.location, obj) 2569 2570 name = self.name.resolve(scope, None) 2571 return IDLWrapperType(self.location, obj) 2572 2573 def withExtendedAttributes(self, attrs): 2574 return IDLUnresolvedType(self.location, self.name, attrs) 2575 2576 def isDistinguishableFrom(self, other): 2577 raise TypeError( 2578 "Can't tell whether an unresolved type is or is not " 2579 "distinguishable from other things" 2580 ) 2581 2582 2583class IDLParametrizedType(IDLType): 2584 def __init__(self, location, name, innerType): 2585 IDLType.__init__(self, location, name) 2586 self.builtin = False 2587 self.inner = innerType 2588 2589 def includesRestrictedFloat(self): 2590 return self.inner.includesRestrictedFloat() 2591 2592 def resolveType(self, parentScope): 2593 assert isinstance(parentScope, IDLScope) 2594 self.inner.resolveType(parentScope) 2595 2596 def isComplete(self): 2597 return self.inner.isComplete() 2598 2599 def unroll(self): 2600 return self.inner.unroll() 2601 2602 def _getDependentObjects(self): 2603 return self.inner._getDependentObjects() 2604 2605 2606class IDLNullableType(IDLParametrizedType): 2607 def __init__(self, location, innerType): 2608 assert not innerType.isVoid() 2609 assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] 2610 2611 IDLParametrizedType.__init__(self, location, None, innerType) 2612 2613 def __hash__(self): 2614 return hash(self.inner) 2615 2616 def __eq__(self, other): 2617 return isinstance(other, IDLNullableType) and self.inner == other.inner 2618 2619 def __str__(self): 2620 return self.inner.__str__() + "OrNull" 2621 2622 def prettyName(self): 2623 return self.inner.prettyName() + "?" 2624 2625 def nullable(self): 2626 return True 2627 2628 def isCallback(self): 2629 return self.inner.isCallback() 2630 2631 def isPrimitive(self): 2632 return self.inner.isPrimitive() 2633 2634 def isBoolean(self): 2635 return self.inner.isBoolean() 2636 2637 def isNumeric(self): 2638 return self.inner.isNumeric() 2639 2640 def isString(self): 2641 return self.inner.isString() 2642 2643 def isByteString(self): 2644 return self.inner.isByteString() 2645 2646 def isDOMString(self): 2647 return self.inner.isDOMString() 2648 2649 def isUSVString(self): 2650 return self.inner.isUSVString() 2651 2652 def isUTF8String(self): 2653 return self.inner.isUTF8String() 2654 2655 def isJSString(self): 2656 return self.inner.isJSString() 2657 2658 def isFloat(self): 2659 return self.inner.isFloat() 2660 2661 def isUnrestricted(self): 2662 return self.inner.isUnrestricted() 2663 2664 def isInteger(self): 2665 return self.inner.isInteger() 2666 2667 def isVoid(self): 2668 return False 2669 2670 def isSequence(self): 2671 return self.inner.isSequence() 2672 2673 def isRecord(self): 2674 return self.inner.isRecord() 2675 2676 def isReadableStream(self): 2677 return self.inner.isReadableStream() 2678 2679 def isArrayBuffer(self): 2680 return self.inner.isArrayBuffer() 2681 2682 def isArrayBufferView(self): 2683 return self.inner.isArrayBufferView() 2684 2685 def isTypedArray(self): 2686 return self.inner.isTypedArray() 2687 2688 def isDictionary(self): 2689 return self.inner.isDictionary() 2690 2691 def isInterface(self): 2692 return self.inner.isInterface() 2693 2694 def isPromise(self): 2695 # There is no such thing as a nullable Promise. 2696 assert not self.inner.isPromise() 2697 return False 2698 2699 def isCallbackInterface(self): 2700 return self.inner.isCallbackInterface() 2701 2702 def isNonCallbackInterface(self): 2703 return self.inner.isNonCallbackInterface() 2704 2705 def isEnum(self): 2706 return self.inner.isEnum() 2707 2708 def isUnion(self): 2709 return self.inner.isUnion() 2710 2711 def isJSONType(self): 2712 return self.inner.isJSONType() 2713 2714 def hasClamp(self): 2715 return self.inner.hasClamp() 2716 2717 def hasEnforceRange(self): 2718 return self.inner.hasEnforceRange() 2719 2720 def hasAllowShared(self): 2721 return self.inner.hasAllowShared() 2722 2723 def isComplete(self): 2724 return self.name is not None 2725 2726 def tag(self): 2727 return self.inner.tag() 2728 2729 def complete(self, scope): 2730 if not self.inner.isComplete(): 2731 self.inner = self.inner.complete(scope) 2732 assert self.inner.isComplete() 2733 2734 if self.inner.nullable(): 2735 raise WebIDLError( 2736 "The inner type of a nullable type must not be " "a nullable type", 2737 [self.location, self.inner.location], 2738 ) 2739 if self.inner.isUnion(): 2740 if self.inner.hasNullableType: 2741 raise WebIDLError( 2742 "The inner type of a nullable type must not " 2743 "be a union type that itself has a nullable " 2744 "type as a member type", 2745 [self.location], 2746 ) 2747 if self.inner.isDOMString(): 2748 if self.inner.legacyNullToEmptyString: 2749 raise WebIDLError( 2750 "[LegacyNullToEmptyString] not allowed on a nullable DOMString", 2751 [self.location, self.inner.location], 2752 ) 2753 2754 self.name = self.inner.name + "OrNull" 2755 return self 2756 2757 def isDistinguishableFrom(self, other): 2758 if ( 2759 other.nullable() 2760 or other.isDictionary() 2761 or ( 2762 other.isUnion() and (other.hasNullableType or other.hasDictionaryType()) 2763 ) 2764 ): 2765 # Can't tell which type null should become 2766 return False 2767 return self.inner.isDistinguishableFrom(other) 2768 2769 def withExtendedAttributes(self, attrs): 2770 # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350 2771 # Allowing extended attributes to apply to a nullable type is an intermediate solution. 2772 # A potential longer term solution is to introduce a null type and get rid of nullables. 2773 # For example, we could do `([Clamp] long or null) foo` in the future. 2774 return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs)) 2775 2776 2777class IDLSequenceType(IDLParametrizedType): 2778 def __init__(self, location, parameterType): 2779 assert not parameterType.isVoid() 2780 2781 IDLParametrizedType.__init__(self, location, parameterType.name, parameterType) 2782 # Need to set self.name up front if our inner type is already complete, 2783 # since in that case our .complete() won't be called. 2784 if self.inner.isComplete(): 2785 self.name = self.inner.name + "Sequence" 2786 2787 def __hash__(self): 2788 return hash(self.inner) 2789 2790 def __eq__(self, other): 2791 return isinstance(other, IDLSequenceType) and self.inner == other.inner 2792 2793 def __str__(self): 2794 return self.inner.__str__() + "Sequence" 2795 2796 def prettyName(self): 2797 return "sequence<%s>" % self.inner.prettyName() 2798 2799 def isVoid(self): 2800 return False 2801 2802 def isSequence(self): 2803 return True 2804 2805 def isJSONType(self): 2806 return self.inner.isJSONType() 2807 2808 def tag(self): 2809 return IDLType.Tags.sequence 2810 2811 def complete(self, scope): 2812 self.inner = self.inner.complete(scope) 2813 self.name = self.inner.name + "Sequence" 2814 return self 2815 2816 def isDistinguishableFrom(self, other): 2817 if other.isPromise(): 2818 return False 2819 if other.isUnion(): 2820 # Just forward to the union; it'll deal 2821 return other.isDistinguishableFrom(self) 2822 return ( 2823 other.isPrimitive() 2824 or other.isString() 2825 or other.isEnum() 2826 or other.isInterface() 2827 or other.isDictionary() 2828 or other.isCallback() 2829 or other.isRecord() 2830 ) 2831 2832 2833class IDLRecordType(IDLParametrizedType): 2834 def __init__(self, location, keyType, valueType): 2835 assert keyType.isString() 2836 assert keyType.isComplete() 2837 assert not valueType.isVoid() 2838 2839 IDLParametrizedType.__init__(self, location, valueType.name, valueType) 2840 self.keyType = keyType 2841 2842 # Need to set self.name up front if our inner type is already complete, 2843 # since in that case our .complete() won't be called. 2844 if self.inner.isComplete(): 2845 self.name = self.keyType.name + self.inner.name + "Record" 2846 2847 def __hash__(self): 2848 return hash(self.inner) 2849 2850 def __eq__(self, other): 2851 return isinstance(other, IDLRecordType) and self.inner == other.inner 2852 2853 def __str__(self): 2854 return self.keyType.__str__() + self.inner.__str__() + "Record" 2855 2856 def prettyName(self): 2857 return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName()) 2858 2859 def isRecord(self): 2860 return True 2861 2862 def isJSONType(self): 2863 return self.inner.isJSONType() 2864 2865 def tag(self): 2866 return IDLType.Tags.record 2867 2868 def complete(self, scope): 2869 self.inner = self.inner.complete(scope) 2870 self.name = self.keyType.name + self.inner.name + "Record" 2871 return self 2872 2873 def unroll(self): 2874 # We do not unroll our inner. Just stop at ourselves. That 2875 # lets us add headers for both ourselves and our inner as 2876 # needed. 2877 return self 2878 2879 def isDistinguishableFrom(self, other): 2880 if other.isPromise(): 2881 return False 2882 if other.isUnion(): 2883 # Just forward to the union; it'll deal 2884 return other.isDistinguishableFrom(self) 2885 return ( 2886 other.isPrimitive() 2887 or other.isString() 2888 or other.isEnum() 2889 or other.isNonCallbackInterface() 2890 or other.isSequence() 2891 ) 2892 2893 def isExposedInAllOf(self, exposureSet): 2894 return self.inner.unroll().isExposedInAllOf(exposureSet) 2895 2896 2897class IDLUnionType(IDLType): 2898 def __init__(self, location, memberTypes): 2899 IDLType.__init__(self, location, "") 2900 self.memberTypes = memberTypes 2901 self.hasNullableType = False 2902 self._dictionaryType = None 2903 self.flatMemberTypes = None 2904 self.builtin = False 2905 2906 def __eq__(self, other): 2907 return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes 2908 2909 def __hash__(self): 2910 assert self.isComplete() 2911 return self.name.__hash__() 2912 2913 def prettyName(self): 2914 return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")" 2915 2916 def isVoid(self): 2917 return False 2918 2919 def isUnion(self): 2920 return True 2921 2922 def isJSONType(self): 2923 return all(m.isJSONType() for m in self.memberTypes) 2924 2925 def includesRestrictedFloat(self): 2926 return any(t.includesRestrictedFloat() for t in self.memberTypes) 2927 2928 def tag(self): 2929 return IDLType.Tags.union 2930 2931 def resolveType(self, parentScope): 2932 assert isinstance(parentScope, IDLScope) 2933 for t in self.memberTypes: 2934 t.resolveType(parentScope) 2935 2936 def isComplete(self): 2937 return self.flatMemberTypes is not None 2938 2939 def complete(self, scope): 2940 def typeName(type): 2941 if isinstance(type, IDLNullableType): 2942 return typeName(type.inner) + "OrNull" 2943 if isinstance(type, IDLWrapperType): 2944 return typeName(type._identifier.object()) 2945 if isinstance(type, IDLObjectWithIdentifier): 2946 return typeName(type.identifier) 2947 if isinstance(type, IDLBuiltinType) and type.hasAllowShared(): 2948 assert type.isBufferSource() 2949 return "MaybeShared" + type.name 2950 return type.name 2951 2952 for (i, type) in enumerate(self.memberTypes): 2953 if not type.isComplete(): 2954 self.memberTypes[i] = type.complete(scope) 2955 2956 self.name = "Or".join(typeName(type) for type in self.memberTypes) 2957 self.flatMemberTypes = list(self.memberTypes) 2958 i = 0 2959 while i < len(self.flatMemberTypes): 2960 if self.flatMemberTypes[i].nullable(): 2961 if self.hasNullableType: 2962 raise WebIDLError( 2963 "Can't have more than one nullable types in a union", 2964 [nullableType.location, self.flatMemberTypes[i].location], 2965 ) 2966 if self.hasDictionaryType(): 2967 raise WebIDLError( 2968 "Can't have a nullable type and a " 2969 "dictionary type in a union", 2970 [ 2971 self._dictionaryType.location, 2972 self.flatMemberTypes[i].location, 2973 ], 2974 ) 2975 self.hasNullableType = True 2976 nullableType = self.flatMemberTypes[i] 2977 self.flatMemberTypes[i] = self.flatMemberTypes[i].inner 2978 continue 2979 if self.flatMemberTypes[i].isDictionary(): 2980 if self.hasNullableType: 2981 raise WebIDLError( 2982 "Can't have a nullable type and a " 2983 "dictionary type in a union", 2984 [nullableType.location, self.flatMemberTypes[i].location], 2985 ) 2986 self._dictionaryType = self.flatMemberTypes[i] 2987 elif self.flatMemberTypes[i].isUnion(): 2988 self.flatMemberTypes[i : i + 1] = self.flatMemberTypes[i].memberTypes 2989 continue 2990 i += 1 2991 2992 for (i, t) in enumerate(self.flatMemberTypes[:-1]): 2993 for u in self.flatMemberTypes[i + 1 :]: 2994 if not t.isDistinguishableFrom(u): 2995 raise WebIDLError( 2996 "Flat member types of a union should be " 2997 "distinguishable, " + str(t) + " is not " 2998 "distinguishable from " + str(u), 2999 [self.location, t.location, u.location], 3000 ) 3001 3002 return self 3003 3004 def isDistinguishableFrom(self, other): 3005 if self.hasNullableType and other.nullable(): 3006 # Can't tell which type null should become 3007 return False 3008 if other.isUnion(): 3009 otherTypes = other.unroll().memberTypes 3010 else: 3011 otherTypes = [other] 3012 # For every type in otherTypes, check that it's distinguishable from 3013 # every type in our types 3014 for u in otherTypes: 3015 if any(not t.isDistinguishableFrom(u) for t in self.memberTypes): 3016 return False 3017 return True 3018 3019 def isExposedInAllOf(self, exposureSet): 3020 # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it. 3021 for globalName in exposureSet: 3022 if not any( 3023 t.unroll().isExposedInAllOf(set([globalName])) 3024 for t in self.flatMemberTypes 3025 ): 3026 return False 3027 return True 3028 3029 def hasDictionaryType(self): 3030 return self._dictionaryType is not None 3031 3032 def hasPossiblyEmptyDictionaryType(self): 3033 return ( 3034 self._dictionaryType is not None and self._dictionaryType.inner.canBeEmpty() 3035 ) 3036 3037 def _getDependentObjects(self): 3038 return set(self.memberTypes) 3039 3040 3041class IDLTypedefType(IDLType): 3042 def __init__(self, location, innerType, name): 3043 IDLType.__init__(self, location, name) 3044 self.inner = innerType 3045 self.builtin = False 3046 3047 def __hash__(self): 3048 return hash(self.inner) 3049 3050 def __eq__(self, other): 3051 return isinstance(other, IDLTypedefType) and self.inner == other.inner 3052 3053 def __str__(self): 3054 return self.name 3055 3056 def nullable(self): 3057 return self.inner.nullable() 3058 3059 def isPrimitive(self): 3060 return self.inner.isPrimitive() 3061 3062 def isBoolean(self): 3063 return self.inner.isBoolean() 3064 3065 def isNumeric(self): 3066 return self.inner.isNumeric() 3067 3068 def isString(self): 3069 return self.inner.isString() 3070 3071 def isByteString(self): 3072 return self.inner.isByteString() 3073 3074 def isDOMString(self): 3075 return self.inner.isDOMString() 3076 3077 def isUSVString(self): 3078 return self.inner.isUSVString() 3079 3080 def isUTF8String(self): 3081 return self.inner.isUTF8String() 3082 3083 def isJSString(self): 3084 return self.inner.isJSString() 3085 3086 def isVoid(self): 3087 return self.inner.isVoid() 3088 3089 def isJSONType(self): 3090 return self.inner.isJSONType() 3091 3092 def isSequence(self): 3093 return self.inner.isSequence() 3094 3095 def isRecord(self): 3096 return self.inner.isRecord() 3097 3098 def isReadableStream(self): 3099 return self.inner.isReadableStream() 3100 3101 def isDictionary(self): 3102 return self.inner.isDictionary() 3103 3104 def isArrayBuffer(self): 3105 return self.inner.isArrayBuffer() 3106 3107 def isArrayBufferView(self): 3108 return self.inner.isArrayBufferView() 3109 3110 def isTypedArray(self): 3111 return self.inner.isTypedArray() 3112 3113 def isInterface(self): 3114 return self.inner.isInterface() 3115 3116 def isCallbackInterface(self): 3117 return self.inner.isCallbackInterface() 3118 3119 def isNonCallbackInterface(self): 3120 return self.inner.isNonCallbackInterface() 3121 3122 def isComplete(self): 3123 return False 3124 3125 def complete(self, parentScope): 3126 if not self.inner.isComplete(): 3127 self.inner = self.inner.complete(parentScope) 3128 assert self.inner.isComplete() 3129 return self.inner 3130 3131 # Do we need a resolveType impl? I don't think it's particularly useful.... 3132 3133 def tag(self): 3134 return self.inner.tag() 3135 3136 def unroll(self): 3137 return self.inner.unroll() 3138 3139 def isDistinguishableFrom(self, other): 3140 return self.inner.isDistinguishableFrom(other) 3141 3142 def _getDependentObjects(self): 3143 return self.inner._getDependentObjects() 3144 3145 def withExtendedAttributes(self, attrs): 3146 return IDLTypedefType( 3147 self.location, self.inner.withExtendedAttributes(attrs), self.name 3148 ) 3149 3150 3151class IDLTypedef(IDLObjectWithIdentifier): 3152 def __init__(self, location, parentScope, innerType, name): 3153 # Set self.innerType first, because IDLObjectWithIdentifier.__init__ 3154 # will call our __str__, which wants to use it. 3155 self.innerType = innerType 3156 identifier = IDLUnresolvedIdentifier(location, name) 3157 IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) 3158 3159 def __str__(self): 3160 return "Typedef %s %s" % (self.identifier.name, self.innerType) 3161 3162 def finish(self, parentScope): 3163 if not self.innerType.isComplete(): 3164 self.innerType = self.innerType.complete(parentScope) 3165 3166 def validate(self): 3167 pass 3168 3169 def isTypedef(self): 3170 return True 3171 3172 def addExtendedAttributes(self, attrs): 3173 if len(attrs) != 0: 3174 raise WebIDLError( 3175 "There are no extended attributes that are " "allowed on typedefs", 3176 [attrs[0].location, self.location], 3177 ) 3178 3179 def _getDependentObjects(self): 3180 return self.innerType._getDependentObjects() 3181 3182 3183class IDLWrapperType(IDLType): 3184 def __init__(self, location, inner): 3185 IDLType.__init__(self, location, inner.identifier.name) 3186 self.inner = inner 3187 self._identifier = inner.identifier 3188 self.builtin = False 3189 3190 def __hash__(self): 3191 return hash(self._identifier) + hash(self.builtin) 3192 3193 def __eq__(self, other): 3194 return ( 3195 isinstance(other, IDLWrapperType) 3196 and self._identifier == other._identifier 3197 and self.builtin == other.builtin 3198 ) 3199 3200 def __str__(self): 3201 return str(self.name) + " (Wrapper)" 3202 3203 def isVoid(self): 3204 return False 3205 3206 def isDictionary(self): 3207 return isinstance(self.inner, IDLDictionary) 3208 3209 def isInterface(self): 3210 return isinstance(self.inner, IDLInterface) or isinstance( 3211 self.inner, IDLExternalInterface 3212 ) 3213 3214 def isCallbackInterface(self): 3215 return self.isInterface() and self.inner.isCallback() 3216 3217 def isNonCallbackInterface(self): 3218 return self.isInterface() and not self.inner.isCallback() 3219 3220 def isEnum(self): 3221 return isinstance(self.inner, IDLEnum) 3222 3223 def isJSONType(self): 3224 if self.isInterface(): 3225 if self.inner.isExternal(): 3226 return False 3227 iface = self.inner 3228 while iface: 3229 if any(m.isMethod() and m.isToJSON() for m in iface.members): 3230 return True 3231 iface = iface.parent 3232 return False 3233 elif self.isEnum(): 3234 return True 3235 elif self.isDictionary(): 3236 dictionary = self.inner 3237 while dictionary: 3238 if not all(m.type.isJSONType() for m in dictionary.members): 3239 return False 3240 dictionary = dictionary.parent 3241 return True 3242 else: 3243 raise WebIDLError( 3244 "IDLWrapperType wraps type %s that we don't know if " 3245 "is serializable" % type(self.inner), 3246 [self.location], 3247 ) 3248 3249 def resolveType(self, parentScope): 3250 assert isinstance(parentScope, IDLScope) 3251 self.inner.resolve(parentScope) 3252 3253 def isComplete(self): 3254 return True 3255 3256 def tag(self): 3257 if self.isInterface(): 3258 return IDLType.Tags.interface 3259 elif self.isEnum(): 3260 return IDLType.Tags.enum 3261 elif self.isDictionary(): 3262 return IDLType.Tags.dictionary 3263 else: 3264 assert False 3265 3266 def isDistinguishableFrom(self, other): 3267 if other.isPromise(): 3268 return False 3269 if other.isUnion(): 3270 # Just forward to the union; it'll deal 3271 return other.isDistinguishableFrom(self) 3272 assert self.isInterface() or self.isEnum() or self.isDictionary() 3273 if self.isEnum(): 3274 return ( 3275 other.isPrimitive() 3276 or other.isInterface() 3277 or other.isObject() 3278 or other.isCallback() 3279 or other.isDictionary() 3280 or other.isSequence() 3281 or other.isRecord() 3282 ) 3283 if self.isDictionary() and other.nullable(): 3284 return False 3285 if ( 3286 other.isPrimitive() 3287 or other.isString() 3288 or other.isEnum() 3289 or other.isSequence() 3290 ): 3291 return True 3292 if self.isDictionary(): 3293 return other.isNonCallbackInterface() 3294 3295 assert self.isInterface() 3296 if other.isInterface(): 3297 if other.isSpiderMonkeyInterface(): 3298 # Just let |other| handle things 3299 return other.isDistinguishableFrom(self) 3300 assert self.isGeckoInterface() and other.isGeckoInterface() 3301 if self.inner.isExternal() or other.unroll().inner.isExternal(): 3302 return self != other 3303 return len( 3304 self.inner.interfacesBasedOnSelf 3305 & other.unroll().inner.interfacesBasedOnSelf 3306 ) == 0 and (self.isNonCallbackInterface() or other.isNonCallbackInterface()) 3307 if other.isDictionary() or other.isCallback() or other.isRecord(): 3308 return self.isNonCallbackInterface() 3309 3310 # Not much else |other| can be 3311 assert other.isObject() 3312 return False 3313 3314 def isExposedInAllOf(self, exposureSet): 3315 if not self.isInterface(): 3316 return True 3317 iface = self.inner 3318 if iface.isExternal(): 3319 # Let's say true, so we don't have to implement exposure mixins on 3320 # external interfaces and sprinkle [Exposed=Window] on every single 3321 # external interface declaration. 3322 return True 3323 return iface.exposureSet.issuperset(exposureSet) 3324 3325 def _getDependentObjects(self): 3326 # NB: The codegen for an interface type depends on 3327 # a) That the identifier is in fact an interface (as opposed to 3328 # a dictionary or something else). 3329 # b) The native type of the interface. 3330 # If we depend on the interface object we will also depend on 3331 # anything the interface depends on which is undesirable. We 3332 # considered implementing a dependency just on the interface type 3333 # file, but then every modification to an interface would cause this 3334 # to be regenerated which is still undesirable. We decided not to 3335 # depend on anything, reasoning that: 3336 # 1) Changing the concrete type of the interface requires modifying 3337 # Bindings.conf, which is still a global dependency. 3338 # 2) Changing an interface to a dictionary (or vice versa) with the 3339 # same identifier should be incredibly rare. 3340 # 3341 # On the other hand, if our type is a dictionary, we should 3342 # depend on it, because the member types of a dictionary 3343 # affect whether a method taking the dictionary as an argument 3344 # takes a JSContext* argument or not. 3345 if self.isDictionary(): 3346 return set([self.inner]) 3347 return set() 3348 3349 3350class IDLPromiseType(IDLParametrizedType): 3351 def __init__(self, location, innerType): 3352 IDLParametrizedType.__init__(self, location, "Promise", innerType) 3353 3354 def __hash__(self): 3355 return hash(self.promiseInnerType()) 3356 3357 def __eq__(self, other): 3358 return ( 3359 isinstance(other, IDLPromiseType) 3360 and self.promiseInnerType() == other.promiseInnerType() 3361 ) 3362 3363 def __str__(self): 3364 return self.inner.__str__() + "Promise" 3365 3366 def prettyName(self): 3367 return "Promise<%s>" % self.inner.prettyName() 3368 3369 def isPromise(self): 3370 return True 3371 3372 def promiseInnerType(self): 3373 return self.inner 3374 3375 def tag(self): 3376 return IDLType.Tags.promise 3377 3378 def complete(self, scope): 3379 self.inner = self.promiseInnerType().complete(scope) 3380 return self 3381 3382 def unroll(self): 3383 # We do not unroll our inner. Just stop at ourselves. That 3384 # lets us add headers for both ourselves and our inner as 3385 # needed. 3386 return self 3387 3388 def isDistinguishableFrom(self, other): 3389 # Promises are not distinguishable from anything. 3390 return False 3391 3392 def isExposedInAllOf(self, exposureSet): 3393 # Check the internal type 3394 return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet) 3395 3396 3397class IDLBuiltinType(IDLType): 3398 3399 Types = enum( 3400 # The integer types 3401 "byte", 3402 "octet", 3403 "short", 3404 "unsigned_short", 3405 "long", 3406 "unsigned_long", 3407 "long_long", 3408 "unsigned_long_long", 3409 # Additional primitive types 3410 "boolean", 3411 "unrestricted_float", 3412 "float", 3413 "unrestricted_double", 3414 # IMPORTANT: "double" must be the last primitive type listed 3415 "double", 3416 # Other types 3417 "any", 3418 "domstring", 3419 "bytestring", 3420 "usvstring", 3421 "utf8string", 3422 "jsstring", 3423 "object", 3424 "void", 3425 # Funny stuff 3426 "ArrayBuffer", 3427 "ArrayBufferView", 3428 "Int8Array", 3429 "Uint8Array", 3430 "Uint8ClampedArray", 3431 "Int16Array", 3432 "Uint16Array", 3433 "Int32Array", 3434 "Uint32Array", 3435 "Float32Array", 3436 "Float64Array", 3437 "ReadableStream", 3438 ) 3439 3440 TagLookup = { 3441 Types.byte: IDLType.Tags.int8, 3442 Types.octet: IDLType.Tags.uint8, 3443 Types.short: IDLType.Tags.int16, 3444 Types.unsigned_short: IDLType.Tags.uint16, 3445 Types.long: IDLType.Tags.int32, 3446 Types.unsigned_long: IDLType.Tags.uint32, 3447 Types.long_long: IDLType.Tags.int64, 3448 Types.unsigned_long_long: IDLType.Tags.uint64, 3449 Types.boolean: IDLType.Tags.bool, 3450 Types.unrestricted_float: IDLType.Tags.unrestricted_float, 3451 Types.float: IDLType.Tags.float, 3452 Types.unrestricted_double: IDLType.Tags.unrestricted_double, 3453 Types.double: IDLType.Tags.double, 3454 Types.any: IDLType.Tags.any, 3455 Types.domstring: IDLType.Tags.domstring, 3456 Types.bytestring: IDLType.Tags.bytestring, 3457 Types.usvstring: IDLType.Tags.usvstring, 3458 Types.utf8string: IDLType.Tags.utf8string, 3459 Types.jsstring: IDLType.Tags.jsstring, 3460 Types.object: IDLType.Tags.object, 3461 Types.void: IDLType.Tags.void, 3462 Types.ArrayBuffer: IDLType.Tags.interface, 3463 Types.ArrayBufferView: IDLType.Tags.interface, 3464 Types.Int8Array: IDLType.Tags.interface, 3465 Types.Uint8Array: IDLType.Tags.interface, 3466 Types.Uint8ClampedArray: IDLType.Tags.interface, 3467 Types.Int16Array: IDLType.Tags.interface, 3468 Types.Uint16Array: IDLType.Tags.interface, 3469 Types.Int32Array: IDLType.Tags.interface, 3470 Types.Uint32Array: IDLType.Tags.interface, 3471 Types.Float32Array: IDLType.Tags.interface, 3472 Types.Float64Array: IDLType.Tags.interface, 3473 Types.ReadableStream: IDLType.Tags.interface, 3474 } 3475 3476 PrettyNames = { 3477 Types.byte: "byte", 3478 Types.octet: "octet", 3479 Types.short: "short", 3480 Types.unsigned_short: "unsigned short", 3481 Types.long: "long", 3482 Types.unsigned_long: "unsigned long", 3483 Types.long_long: "long long", 3484 Types.unsigned_long_long: "unsigned long long", 3485 Types.boolean: "boolean", 3486 Types.unrestricted_float: "unrestricted float", 3487 Types.float: "float", 3488 Types.unrestricted_double: "unrestricted double", 3489 Types.double: "double", 3490 Types.any: "any", 3491 Types.domstring: "DOMString", 3492 Types.bytestring: "ByteString", 3493 Types.usvstring: "USVString", 3494 Types.utf8string: "USVString", # That's what it is in spec terms 3495 Types.jsstring: "USVString", # Again, that's what it is in spec terms 3496 Types.object: "object", 3497 Types.void: "void", 3498 Types.ArrayBuffer: "ArrayBuffer", 3499 Types.ArrayBufferView: "ArrayBufferView", 3500 Types.Int8Array: "Int8Array", 3501 Types.Uint8Array: "Uint8Array", 3502 Types.Uint8ClampedArray: "Uint8ClampedArray", 3503 Types.Int16Array: "Int16Array", 3504 Types.Uint16Array: "Uint16Array", 3505 Types.Int32Array: "Int32Array", 3506 Types.Uint32Array: "Uint32Array", 3507 Types.Float32Array: "Float32Array", 3508 Types.Float64Array: "Float64Array", 3509 Types.ReadableStream: "ReadableStream", 3510 } 3511 3512 def __init__( 3513 self, 3514 location, 3515 name, 3516 type, 3517 clamp=False, 3518 enforceRange=False, 3519 legacyNullToEmptyString=False, 3520 allowShared=False, 3521 attrLocation=[], 3522 ): 3523 """ 3524 The mutually exclusive clamp/enforceRange/legacyNullToEmptyString/allowShared arguments are used 3525 to create instances of this type with the appropriate attributes attached. Use .clamped(), 3526 .rangeEnforced(), .withLegacyNullToEmptyString() and .withAllowShared(). 3527 3528 attrLocation is an array of source locations of these attributes for error reporting. 3529 """ 3530 IDLType.__init__(self, location, name) 3531 self.builtin = True 3532 self._typeTag = type 3533 self._clamped = None 3534 self._rangeEnforced = None 3535 self._withLegacyNullToEmptyString = None 3536 self._withAllowShared = None 3537 if self.isInteger(): 3538 if clamp: 3539 self._clamp = True 3540 self.name = "Clamped" + self.name 3541 self._extendedAttrDict["Clamp"] = True 3542 elif enforceRange: 3543 self._enforceRange = True 3544 self.name = "RangeEnforced" + self.name 3545 self._extendedAttrDict["EnforceRange"] = True 3546 elif clamp or enforceRange: 3547 raise WebIDLError( 3548 "Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation 3549 ) 3550 if self.isDOMString() or self.isUTF8String(): 3551 if legacyNullToEmptyString: 3552 self.legacyNullToEmptyString = True 3553 self.name = "NullIsEmpty" + self.name 3554 self._extendedAttrDict["LegacyNullToEmptyString"] = True 3555 elif legacyNullToEmptyString: 3556 raise WebIDLError( 3557 "Non-string types cannot be [LegacyNullToEmptyString]", attrLocation 3558 ) 3559 if self.isBufferSource(): 3560 if allowShared: 3561 self._allowShared = True 3562 self._extendedAttrDict["AllowShared"] = True 3563 elif allowShared: 3564 raise WebIDLError( 3565 "Types that are not buffer source types cannot be [AllowShared]", 3566 attrLocation, 3567 ) 3568 3569 def __str__(self): 3570 if self._allowShared: 3571 assert self.isBufferSource() 3572 return "MaybeShared" + str(self.name) 3573 return str(self.name) 3574 3575 def prettyName(self): 3576 return IDLBuiltinType.PrettyNames[self._typeTag] 3577 3578 def clamped(self, attrLocation): 3579 if not self._clamped: 3580 self._clamped = IDLBuiltinType( 3581 self.location, 3582 self.name, 3583 self._typeTag, 3584 clamp=True, 3585 attrLocation=attrLocation, 3586 ) 3587 return self._clamped 3588 3589 def rangeEnforced(self, attrLocation): 3590 if not self._rangeEnforced: 3591 self._rangeEnforced = IDLBuiltinType( 3592 self.location, 3593 self.name, 3594 self._typeTag, 3595 enforceRange=True, 3596 attrLocation=attrLocation, 3597 ) 3598 return self._rangeEnforced 3599 3600 def withLegacyNullToEmptyString(self, attrLocation): 3601 if not self._withLegacyNullToEmptyString: 3602 self._withLegacyNullToEmptyString = IDLBuiltinType( 3603 self.location, 3604 self.name, 3605 self._typeTag, 3606 legacyNullToEmptyString=True, 3607 attrLocation=attrLocation, 3608 ) 3609 return self._withLegacyNullToEmptyString 3610 3611 def withAllowShared(self, attrLocation): 3612 if not self._withAllowShared: 3613 self._withAllowShared = IDLBuiltinType( 3614 self.location, 3615 self.name, 3616 self._typeTag, 3617 allowShared=True, 3618 attrLocation=attrLocation, 3619 ) 3620 return self._withAllowShared 3621 3622 def isPrimitive(self): 3623 return self._typeTag <= IDLBuiltinType.Types.double 3624 3625 def isBoolean(self): 3626 return self._typeTag == IDLBuiltinType.Types.boolean 3627 3628 def isNumeric(self): 3629 return self.isPrimitive() and not self.isBoolean() 3630 3631 def isString(self): 3632 return ( 3633 self._typeTag == IDLBuiltinType.Types.domstring 3634 or self._typeTag == IDLBuiltinType.Types.bytestring 3635 or self._typeTag == IDLBuiltinType.Types.usvstring 3636 or self._typeTag == IDLBuiltinType.Types.utf8string 3637 or self._typeTag == IDLBuiltinType.Types.jsstring 3638 ) 3639 3640 def isByteString(self): 3641 return self._typeTag == IDLBuiltinType.Types.bytestring 3642 3643 def isDOMString(self): 3644 return self._typeTag == IDLBuiltinType.Types.domstring 3645 3646 def isUSVString(self): 3647 return self._typeTag == IDLBuiltinType.Types.usvstring 3648 3649 def isUTF8String(self): 3650 return self._typeTag == IDLBuiltinType.Types.utf8string 3651 3652 def isJSString(self): 3653 return self._typeTag == IDLBuiltinType.Types.jsstring 3654 3655 def isInteger(self): 3656 return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long 3657 3658 def isArrayBuffer(self): 3659 return self._typeTag == IDLBuiltinType.Types.ArrayBuffer 3660 3661 def isArrayBufferView(self): 3662 return self._typeTag == IDLBuiltinType.Types.ArrayBufferView 3663 3664 def isTypedArray(self): 3665 return ( 3666 self._typeTag >= IDLBuiltinType.Types.Int8Array 3667 and self._typeTag <= IDLBuiltinType.Types.Float64Array 3668 ) 3669 3670 def isReadableStream(self): 3671 return self._typeTag == IDLBuiltinType.Types.ReadableStream 3672 3673 def isInterface(self): 3674 # TypedArray things are interface types per the TypedArray spec, 3675 # but we handle them as builtins because SpiderMonkey implements 3676 # all of it internally. 3677 return ( 3678 self.isArrayBuffer() 3679 or self.isArrayBufferView() 3680 or self.isTypedArray() 3681 or self.isReadableStream() 3682 ) 3683 3684 def isNonCallbackInterface(self): 3685 # All the interfaces we can be are non-callback 3686 return self.isInterface() 3687 3688 def isFloat(self): 3689 return ( 3690 self._typeTag == IDLBuiltinType.Types.float 3691 or self._typeTag == IDLBuiltinType.Types.double 3692 or self._typeTag == IDLBuiltinType.Types.unrestricted_float 3693 or self._typeTag == IDLBuiltinType.Types.unrestricted_double 3694 ) 3695 3696 def isUnrestricted(self): 3697 assert self.isFloat() 3698 return ( 3699 self._typeTag == IDLBuiltinType.Types.unrestricted_float 3700 or self._typeTag == IDLBuiltinType.Types.unrestricted_double 3701 ) 3702 3703 def isJSONType(self): 3704 return self.isPrimitive() or self.isString() or self.isObject() 3705 3706 def includesRestrictedFloat(self): 3707 return self.isFloat() and not self.isUnrestricted() 3708 3709 def tag(self): 3710 return IDLBuiltinType.TagLookup[self._typeTag] 3711 3712 def isDistinguishableFrom(self, other): 3713 if other.isPromise(): 3714 return False 3715 if other.isUnion(): 3716 # Just forward to the union; it'll deal 3717 return other.isDistinguishableFrom(self) 3718 if self.isBoolean(): 3719 return ( 3720 other.isNumeric() 3721 or other.isString() 3722 or other.isEnum() 3723 or other.isInterface() 3724 or other.isObject() 3725 or other.isCallback() 3726 or other.isDictionary() 3727 or other.isSequence() 3728 or other.isRecord() 3729 ) 3730 if self.isNumeric(): 3731 return ( 3732 other.isBoolean() 3733 or other.isString() 3734 or other.isEnum() 3735 or other.isInterface() 3736 or other.isObject() 3737 or other.isCallback() 3738 or other.isDictionary() 3739 or other.isSequence() 3740 or other.isRecord() 3741 ) 3742 if self.isString(): 3743 return ( 3744 other.isPrimitive() 3745 or other.isInterface() 3746 or other.isObject() 3747 or other.isCallback() 3748 or other.isDictionary() 3749 or other.isSequence() 3750 or other.isRecord() 3751 ) 3752 if self.isAny(): 3753 # Can't tell "any" apart from anything 3754 return False 3755 if self.isObject(): 3756 return other.isPrimitive() or other.isString() or other.isEnum() 3757 if self.isVoid(): 3758 return not other.isVoid() 3759 # Not much else we could be! 3760 assert self.isSpiderMonkeyInterface() 3761 # Like interfaces, but we know we're not a callback 3762 return ( 3763 other.isPrimitive() 3764 or other.isString() 3765 or other.isEnum() 3766 or other.isCallback() 3767 or other.isDictionary() 3768 or other.isSequence() 3769 or other.isRecord() 3770 or ( 3771 other.isInterface() 3772 and ( 3773 # ArrayBuffer is distinguishable from everything 3774 # that's not an ArrayBuffer or a callback interface 3775 (self.isArrayBuffer() and not other.isArrayBuffer()) 3776 or (self.isReadableStream() and not other.isReadableStream()) 3777 or 3778 # ArrayBufferView is distinguishable from everything 3779 # that's not an ArrayBufferView or typed array. 3780 ( 3781 self.isArrayBufferView() 3782 and not other.isArrayBufferView() 3783 and not other.isTypedArray() 3784 ) 3785 or 3786 # Typed arrays are distinguishable from everything 3787 # except ArrayBufferView and the same type of typed 3788 # array 3789 ( 3790 self.isTypedArray() 3791 and not other.isArrayBufferView() 3792 and not (other.isTypedArray() and other.name == self.name) 3793 ) 3794 ) 3795 ) 3796 ) 3797 3798 def _getDependentObjects(self): 3799 return set() 3800 3801 def withExtendedAttributes(self, attrs): 3802 ret = self 3803 for attribute in attrs: 3804 identifier = attribute.identifier() 3805 if identifier == "Clamp": 3806 if not attribute.noArguments(): 3807 raise WebIDLError( 3808 "[Clamp] must take no arguments", [attribute.location] 3809 ) 3810 if ret.hasEnforceRange() or self._enforceRange: 3811 raise WebIDLError( 3812 "[EnforceRange] and [Clamp] are mutually exclusive", 3813 [self.location, attribute.location], 3814 ) 3815 ret = self.clamped([self.location, attribute.location]) 3816 elif identifier == "EnforceRange": 3817 if not attribute.noArguments(): 3818 raise WebIDLError( 3819 "[EnforceRange] must take no arguments", [attribute.location] 3820 ) 3821 if ret.hasClamp() or self._clamp: 3822 raise WebIDLError( 3823 "[EnforceRange] and [Clamp] are mutually exclusive", 3824 [self.location, attribute.location], 3825 ) 3826 ret = self.rangeEnforced([self.location, attribute.location]) 3827 elif identifier == "LegacyNullToEmptyString": 3828 if not (self.isDOMString() or self.isUTF8String()): 3829 raise WebIDLError( 3830 "[LegacyNullToEmptyString] only allowed on DOMStrings and UTF8Strings", 3831 [self.location, attribute.location], 3832 ) 3833 assert not self.nullable() 3834 if attribute.hasValue(): 3835 raise WebIDLError( 3836 "[LegacyNullToEmptyString] must take no identifier argument", 3837 [attribute.location], 3838 ) 3839 ret = self.withLegacyNullToEmptyString( 3840 [self.location, attribute.location] 3841 ) 3842 elif identifier == "AllowShared": 3843 if not attribute.noArguments(): 3844 raise WebIDLError( 3845 "[AllowShared] must take no arguments", [attribute.location] 3846 ) 3847 if not self.isBufferSource(): 3848 raise WebIDLError( 3849 "[AllowShared] only allowed on buffer source types", 3850 [self.location, attribute.location], 3851 ) 3852 ret = self.withAllowShared([self.location, attribute.location]) 3853 3854 else: 3855 raise WebIDLError( 3856 "Unhandled extended attribute on type", 3857 [self.location, attribute.location], 3858 ) 3859 return ret 3860 3861 3862BuiltinTypes = { 3863 IDLBuiltinType.Types.byte: IDLBuiltinType( 3864 BuiltinLocation("<builtin type>"), "Byte", IDLBuiltinType.Types.byte 3865 ), 3866 IDLBuiltinType.Types.octet: IDLBuiltinType( 3867 BuiltinLocation("<builtin type>"), "Octet", IDLBuiltinType.Types.octet 3868 ), 3869 IDLBuiltinType.Types.short: IDLBuiltinType( 3870 BuiltinLocation("<builtin type>"), "Short", IDLBuiltinType.Types.short 3871 ), 3872 IDLBuiltinType.Types.unsigned_short: IDLBuiltinType( 3873 BuiltinLocation("<builtin type>"), 3874 "UnsignedShort", 3875 IDLBuiltinType.Types.unsigned_short, 3876 ), 3877 IDLBuiltinType.Types.long: IDLBuiltinType( 3878 BuiltinLocation("<builtin type>"), "Long", IDLBuiltinType.Types.long 3879 ), 3880 IDLBuiltinType.Types.unsigned_long: IDLBuiltinType( 3881 BuiltinLocation("<builtin type>"), 3882 "UnsignedLong", 3883 IDLBuiltinType.Types.unsigned_long, 3884 ), 3885 IDLBuiltinType.Types.long_long: IDLBuiltinType( 3886 BuiltinLocation("<builtin type>"), "LongLong", IDLBuiltinType.Types.long_long 3887 ), 3888 IDLBuiltinType.Types.unsigned_long_long: IDLBuiltinType( 3889 BuiltinLocation("<builtin type>"), 3890 "UnsignedLongLong", 3891 IDLBuiltinType.Types.unsigned_long_long, 3892 ), 3893 IDLBuiltinType.Types.boolean: IDLBuiltinType( 3894 BuiltinLocation("<builtin type>"), "Boolean", IDLBuiltinType.Types.boolean 3895 ), 3896 IDLBuiltinType.Types.float: IDLBuiltinType( 3897 BuiltinLocation("<builtin type>"), "Float", IDLBuiltinType.Types.float 3898 ), 3899 IDLBuiltinType.Types.unrestricted_float: IDLBuiltinType( 3900 BuiltinLocation("<builtin type>"), 3901 "UnrestrictedFloat", 3902 IDLBuiltinType.Types.unrestricted_float, 3903 ), 3904 IDLBuiltinType.Types.double: IDLBuiltinType( 3905 BuiltinLocation("<builtin type>"), "Double", IDLBuiltinType.Types.double 3906 ), 3907 IDLBuiltinType.Types.unrestricted_double: IDLBuiltinType( 3908 BuiltinLocation("<builtin type>"), 3909 "UnrestrictedDouble", 3910 IDLBuiltinType.Types.unrestricted_double, 3911 ), 3912 IDLBuiltinType.Types.any: IDLBuiltinType( 3913 BuiltinLocation("<builtin type>"), "Any", IDLBuiltinType.Types.any 3914 ), 3915 IDLBuiltinType.Types.domstring: IDLBuiltinType( 3916 BuiltinLocation("<builtin type>"), "String", IDLBuiltinType.Types.domstring 3917 ), 3918 IDLBuiltinType.Types.bytestring: IDLBuiltinType( 3919 BuiltinLocation("<builtin type>"), "ByteString", IDLBuiltinType.Types.bytestring 3920 ), 3921 IDLBuiltinType.Types.usvstring: IDLBuiltinType( 3922 BuiltinLocation("<builtin type>"), "USVString", IDLBuiltinType.Types.usvstring 3923 ), 3924 IDLBuiltinType.Types.utf8string: IDLBuiltinType( 3925 BuiltinLocation("<builtin type>"), "UTF8String", IDLBuiltinType.Types.utf8string 3926 ), 3927 IDLBuiltinType.Types.jsstring: IDLBuiltinType( 3928 BuiltinLocation("<builtin type>"), "JSString", IDLBuiltinType.Types.jsstring 3929 ), 3930 IDLBuiltinType.Types.object: IDLBuiltinType( 3931 BuiltinLocation("<builtin type>"), "Object", IDLBuiltinType.Types.object 3932 ), 3933 IDLBuiltinType.Types.void: IDLBuiltinType( 3934 BuiltinLocation("<builtin type>"), "Void", IDLBuiltinType.Types.void 3935 ), 3936 IDLBuiltinType.Types.ArrayBuffer: IDLBuiltinType( 3937 BuiltinLocation("<builtin type>"), 3938 "ArrayBuffer", 3939 IDLBuiltinType.Types.ArrayBuffer, 3940 ), 3941 IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType( 3942 BuiltinLocation("<builtin type>"), 3943 "ArrayBufferView", 3944 IDLBuiltinType.Types.ArrayBufferView, 3945 ), 3946 IDLBuiltinType.Types.Int8Array: IDLBuiltinType( 3947 BuiltinLocation("<builtin type>"), "Int8Array", IDLBuiltinType.Types.Int8Array 3948 ), 3949 IDLBuiltinType.Types.Uint8Array: IDLBuiltinType( 3950 BuiltinLocation("<builtin type>"), "Uint8Array", IDLBuiltinType.Types.Uint8Array 3951 ), 3952 IDLBuiltinType.Types.Uint8ClampedArray: IDLBuiltinType( 3953 BuiltinLocation("<builtin type>"), 3954 "Uint8ClampedArray", 3955 IDLBuiltinType.Types.Uint8ClampedArray, 3956 ), 3957 IDLBuiltinType.Types.Int16Array: IDLBuiltinType( 3958 BuiltinLocation("<builtin type>"), "Int16Array", IDLBuiltinType.Types.Int16Array 3959 ), 3960 IDLBuiltinType.Types.Uint16Array: IDLBuiltinType( 3961 BuiltinLocation("<builtin type>"), 3962 "Uint16Array", 3963 IDLBuiltinType.Types.Uint16Array, 3964 ), 3965 IDLBuiltinType.Types.Int32Array: IDLBuiltinType( 3966 BuiltinLocation("<builtin type>"), "Int32Array", IDLBuiltinType.Types.Int32Array 3967 ), 3968 IDLBuiltinType.Types.Uint32Array: IDLBuiltinType( 3969 BuiltinLocation("<builtin type>"), 3970 "Uint32Array", 3971 IDLBuiltinType.Types.Uint32Array, 3972 ), 3973 IDLBuiltinType.Types.Float32Array: IDLBuiltinType( 3974 BuiltinLocation("<builtin type>"), 3975 "Float32Array", 3976 IDLBuiltinType.Types.Float32Array, 3977 ), 3978 IDLBuiltinType.Types.Float64Array: IDLBuiltinType( 3979 BuiltinLocation("<builtin type>"), 3980 "Float64Array", 3981 IDLBuiltinType.Types.Float64Array, 3982 ), 3983 IDLBuiltinType.Types.ReadableStream: IDLBuiltinType( 3984 BuiltinLocation("<builtin type>"), 3985 "ReadableStream", 3986 IDLBuiltinType.Types.ReadableStream, 3987 ), 3988} 3989 3990 3991integerTypeSizes = { 3992 IDLBuiltinType.Types.byte: (-128, 127), 3993 IDLBuiltinType.Types.octet: (0, 255), 3994 IDLBuiltinType.Types.short: (-32768, 32767), 3995 IDLBuiltinType.Types.unsigned_short: (0, 65535), 3996 IDLBuiltinType.Types.long: (-2147483648, 2147483647), 3997 IDLBuiltinType.Types.unsigned_long: (0, 4294967295), 3998 IDLBuiltinType.Types.long_long: (-9223372036854775808, 9223372036854775807), 3999 IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615), 4000} 4001 4002 4003def matchIntegerValueToType(value): 4004 for type, extremes in integerTypeSizes.items(): 4005 (min, max) = extremes 4006 if value <= max and value >= min: 4007 return BuiltinTypes[type] 4008 4009 return None 4010 4011 4012class NoCoercionFoundError(WebIDLError): 4013 """ 4014 A class we use to indicate generic coercion failures because none of the 4015 types worked out in IDLValue.coerceToType. 4016 """ 4017 4018 4019class IDLValue(IDLObject): 4020 def __init__(self, location, type, value): 4021 IDLObject.__init__(self, location) 4022 self.type = type 4023 assert isinstance(type, IDLType) 4024 4025 self.value = value 4026 4027 def coerceToType(self, type, location): 4028 if type == self.type: 4029 return self # Nothing to do 4030 4031 # We first check for unions to ensure that even if the union is nullable 4032 # we end up with the right flat member type, not the union's type. 4033 if type.isUnion(): 4034 # We use the flat member types here, because if we have a nullable 4035 # member type, or a nested union, we want the type the value 4036 # actually coerces to, not the nullable or nested union type. 4037 for subtype in type.unroll().flatMemberTypes: 4038 try: 4039 coercedValue = self.coerceToType(subtype, location) 4040 # Create a new IDLValue to make sure that we have the 4041 # correct float/double type. This is necessary because we 4042 # use the value's type when it is a default value of a 4043 # union, and the union cares about the exact float type. 4044 return IDLValue(self.location, subtype, coercedValue.value) 4045 except Exception as e: 4046 # Make sure to propagate out WebIDLErrors that are not the 4047 # generic "hey, we could not coerce to this type at all" 4048 # exception, because those are specific "coercion failed for 4049 # reason X" exceptions. Note that we want to swallow 4050 # non-WebIDLErrors here, because those can just happen if 4051 # "type" is not something that can have a default value at 4052 # all. 4053 if isinstance(e, WebIDLError) and not isinstance( 4054 e, NoCoercionFoundError 4055 ): 4056 raise e 4057 4058 # If the type allows null, rerun this matching on the inner type, except 4059 # nullable enums. We handle those specially, because we want our 4060 # default string values to stay strings even when assigned to a nullable 4061 # enum. 4062 elif type.nullable() and not type.isEnum(): 4063 innerValue = self.coerceToType(type.inner, location) 4064 return IDLValue(self.location, type, innerValue.value) 4065 4066 elif self.type.isInteger() and type.isInteger(): 4067 # We're both integer types. See if we fit. 4068 4069 (min, max) = integerTypeSizes[type._typeTag] 4070 if self.value <= max and self.value >= min: 4071 # Promote 4072 return IDLValue(self.location, type, self.value) 4073 else: 4074 raise WebIDLError( 4075 "Value %s is out of range for type %s." % (self.value, type), 4076 [location], 4077 ) 4078 elif self.type.isInteger() and type.isFloat(): 4079 # Convert an integer literal into float 4080 if -(2 ** 24) <= self.value <= 2 ** 24: 4081 return IDLValue(self.location, type, float(self.value)) 4082 else: 4083 raise WebIDLError( 4084 "Converting value %s to %s will lose precision." 4085 % (self.value, type), 4086 [location], 4087 ) 4088 elif self.type.isString() and type.isEnum(): 4089 # Just keep our string, but make sure it's a valid value for this enum 4090 enum = type.unroll().inner 4091 if self.value not in enum.values(): 4092 raise WebIDLError( 4093 "'%s' is not a valid default value for enum %s" 4094 % (self.value, enum.identifier.name), 4095 [location, enum.location], 4096 ) 4097 return self 4098 elif self.type.isFloat() and type.isFloat(): 4099 if not type.isUnrestricted() and ( 4100 self.value == float("inf") 4101 or self.value == float("-inf") 4102 or math.isnan(self.value) 4103 ): 4104 raise WebIDLError( 4105 "Trying to convert unrestricted value %s to non-unrestricted" 4106 % self.value, 4107 [location], 4108 ) 4109 return IDLValue(self.location, type, self.value) 4110 elif self.type.isString() and type.isUSVString(): 4111 # Allow USVStrings to use default value just like 4112 # DOMString. No coercion is required in this case as Codegen.py 4113 # treats USVString just like DOMString, but with an 4114 # extra normalization step. 4115 assert self.type.isDOMString() 4116 return self 4117 elif self.type.isString() and ( 4118 type.isByteString() or type.isJSString() or type.isUTF8String() 4119 ): 4120 # Allow ByteStrings, UTF8String, and JSStrings to use a default 4121 # value like DOMString. 4122 # No coercion is required as Codegen.py will handle the 4123 # extra steps. We want to make sure that our string contains 4124 # only valid characters, so we check that here. 4125 valid_ascii_lit = ( 4126 " " + string.ascii_letters + string.digits + string.punctuation 4127 ) 4128 for idx, c in enumerate(self.value): 4129 if c not in valid_ascii_lit: 4130 raise WebIDLError( 4131 "Coercing this string literal %s to a ByteString is not supported yet. " 4132 "Coercion failed due to an unsupported byte %d at index %d." 4133 % (self.value.__repr__(), ord(c), idx), 4134 [location], 4135 ) 4136 4137 return IDLValue(self.location, type, self.value) 4138 elif self.type.isDOMString() and type.legacyNullToEmptyString: 4139 # LegacyNullToEmptyString is a different type for resolution reasons, 4140 # however once you have a value it doesn't matter 4141 return self 4142 4143 raise NoCoercionFoundError( 4144 "Cannot coerce type %s to type %s." % (self.type, type), [location] 4145 ) 4146 4147 def _getDependentObjects(self): 4148 return set() 4149 4150 4151class IDLNullValue(IDLObject): 4152 def __init__(self, location): 4153 IDLObject.__init__(self, location) 4154 self.type = None 4155 self.value = None 4156 4157 def coerceToType(self, type, location): 4158 if ( 4159 not isinstance(type, IDLNullableType) 4160 and not (type.isUnion() and type.hasNullableType) 4161 and not type.isAny() 4162 ): 4163 raise WebIDLError("Cannot coerce null value to type %s." % type, [location]) 4164 4165 nullValue = IDLNullValue(self.location) 4166 if type.isUnion() and not type.nullable() and type.hasDictionaryType(): 4167 # We're actually a default value for the union's dictionary member. 4168 # Use its type. 4169 for t in type.flatMemberTypes: 4170 if t.isDictionary(): 4171 nullValue.type = t 4172 return nullValue 4173 nullValue.type = type 4174 return nullValue 4175 4176 def _getDependentObjects(self): 4177 return set() 4178 4179 4180class IDLEmptySequenceValue(IDLObject): 4181 def __init__(self, location): 4182 IDLObject.__init__(self, location) 4183 self.type = None 4184 self.value = None 4185 4186 def coerceToType(self, type, location): 4187 if type.isUnion(): 4188 # We use the flat member types here, because if we have a nullable 4189 # member type, or a nested union, we want the type the value 4190 # actually coerces to, not the nullable or nested union type. 4191 for subtype in type.unroll().flatMemberTypes: 4192 try: 4193 return self.coerceToType(subtype, location) 4194 except: 4195 pass 4196 4197 if not type.isSequence(): 4198 raise WebIDLError( 4199 "Cannot coerce empty sequence value to type %s." % type, [location] 4200 ) 4201 4202 emptySequenceValue = IDLEmptySequenceValue(self.location) 4203 emptySequenceValue.type = type 4204 return emptySequenceValue 4205 4206 def _getDependentObjects(self): 4207 return set() 4208 4209 4210class IDLDefaultDictionaryValue(IDLObject): 4211 def __init__(self, location): 4212 IDLObject.__init__(self, location) 4213 self.type = None 4214 self.value = None 4215 4216 def coerceToType(self, type, location): 4217 if type.isUnion(): 4218 # We use the flat member types here, because if we have a nullable 4219 # member type, or a nested union, we want the type the value 4220 # actually coerces to, not the nullable or nested union type. 4221 for subtype in type.unroll().flatMemberTypes: 4222 try: 4223 return self.coerceToType(subtype, location) 4224 except: 4225 pass 4226 4227 if not type.isDictionary(): 4228 raise WebIDLError( 4229 "Cannot coerce default dictionary value to type %s." % type, [location] 4230 ) 4231 4232 defaultDictionaryValue = IDLDefaultDictionaryValue(self.location) 4233 defaultDictionaryValue.type = type 4234 return defaultDictionaryValue 4235 4236 def _getDependentObjects(self): 4237 return set() 4238 4239 4240class IDLUndefinedValue(IDLObject): 4241 def __init__(self, location): 4242 IDLObject.__init__(self, location) 4243 self.type = None 4244 self.value = None 4245 4246 def coerceToType(self, type, location): 4247 if not type.isAny(): 4248 raise WebIDLError( 4249 "Cannot coerce undefined value to type %s." % type, [location] 4250 ) 4251 4252 undefinedValue = IDLUndefinedValue(self.location) 4253 undefinedValue.type = type 4254 return undefinedValue 4255 4256 def _getDependentObjects(self): 4257 return set() 4258 4259 4260class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): 4261 4262 Tags = enum("Const", "Attr", "Method", "MaplikeOrSetlike", "Iterable") 4263 4264 Special = enum("Static", "Stringifier") 4265 4266 AffectsValues = ("Nothing", "Everything") 4267 DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything") 4268 4269 def __init__(self, location, identifier, tag, extendedAttrDict=None): 4270 IDLObjectWithIdentifier.__init__(self, location, None, identifier) 4271 IDLExposureMixins.__init__(self, location) 4272 self.tag = tag 4273 if extendedAttrDict is None: 4274 self._extendedAttrDict = {} 4275 else: 4276 self._extendedAttrDict = extendedAttrDict 4277 4278 def isMethod(self): 4279 return self.tag == IDLInterfaceMember.Tags.Method 4280 4281 def isAttr(self): 4282 return self.tag == IDLInterfaceMember.Tags.Attr 4283 4284 def isConst(self): 4285 return self.tag == IDLInterfaceMember.Tags.Const 4286 4287 def isMaplikeOrSetlikeOrIterable(self): 4288 return ( 4289 self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike 4290 or self.tag == IDLInterfaceMember.Tags.Iterable 4291 ) 4292 4293 def isMaplikeOrSetlike(self): 4294 return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike 4295 4296 def addExtendedAttributes(self, attrs): 4297 for attr in attrs: 4298 self.handleExtendedAttribute(attr) 4299 attrlist = attr.listValue() 4300 self._extendedAttrDict[attr.identifier()] = ( 4301 attrlist if len(attrlist) else True 4302 ) 4303 4304 def handleExtendedAttribute(self, attr): 4305 pass 4306 4307 def getExtendedAttribute(self, name): 4308 return self._extendedAttrDict.get(name, None) 4309 4310 def finish(self, scope): 4311 IDLExposureMixins.finish(self, scope) 4312 4313 def validate(self): 4314 if self.isAttr() or self.isMethod(): 4315 if self.affects == "Everything" and self.dependsOn != "Everything": 4316 raise WebIDLError( 4317 "Interface member is flagged as affecting " 4318 "everything but not depending on everything. " 4319 "That seems rather unlikely.", 4320 [self.location], 4321 ) 4322 4323 if self.getExtendedAttribute("NewObject"): 4324 if self.dependsOn == "Nothing" or self.dependsOn == "DOMState": 4325 raise WebIDLError( 4326 "A [NewObject] method is not idempotent, " 4327 "so it has to depend on something other than DOM state.", 4328 [self.location], 4329 ) 4330 if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( 4331 "StoreInSlot" 4332 ): 4333 raise WebIDLError( 4334 "A [NewObject] attribute shouldnt be " 4335 "[Cached] or [StoreInSlot], since the point " 4336 "of those is to keep returning the same " 4337 "thing across multiple calls, which is not " 4338 "what [NewObject] does.", 4339 [self.location], 4340 ) 4341 4342 def _setDependsOn(self, dependsOn): 4343 if self.dependsOn != "Everything": 4344 raise WebIDLError( 4345 "Trying to specify multiple different DependsOn, " 4346 "Pure, or Constant extended attributes for " 4347 "attribute", 4348 [self.location], 4349 ) 4350 if dependsOn not in IDLInterfaceMember.DependsOnValues: 4351 raise WebIDLError( 4352 "Invalid [DependsOn=%s] on attribute" % dependsOn, [self.location] 4353 ) 4354 self.dependsOn = dependsOn 4355 4356 def _setAffects(self, affects): 4357 if self.affects != "Everything": 4358 raise WebIDLError( 4359 "Trying to specify multiple different Affects, " 4360 "Pure, or Constant extended attributes for " 4361 "attribute", 4362 [self.location], 4363 ) 4364 if affects not in IDLInterfaceMember.AffectsValues: 4365 raise WebIDLError( 4366 "Invalid [Affects=%s] on attribute" % dependsOn, [self.location] 4367 ) 4368 self.affects = affects 4369 4370 def _addAlias(self, alias): 4371 if alias in self.aliases: 4372 raise WebIDLError( 4373 "Duplicate [Alias=%s] on attribute" % alias, [self.location] 4374 ) 4375 self.aliases.append(alias) 4376 4377 def _addBindingAlias(self, bindingAlias): 4378 if bindingAlias in self.bindingAliases: 4379 raise WebIDLError( 4380 "Duplicate [BindingAlias=%s] on attribute" % bindingAlias, 4381 [self.location], 4382 ) 4383 self.bindingAliases.append(bindingAlias) 4384 4385 4386class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): 4387 def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind): 4388 IDLInterfaceMember.__init__(self, location, identifier, ifaceKind) 4389 if keyType is not None: 4390 assert isinstance(keyType, IDLType) 4391 else: 4392 assert valueType is not None 4393 assert ifaceType in ["maplike", "setlike", "iterable"] 4394 if valueType is not None: 4395 assert isinstance(valueType, IDLType) 4396 self.keyType = keyType 4397 self.valueType = valueType 4398 self.maplikeOrSetlikeOrIterableType = ifaceType 4399 self.disallowedMemberNames = [] 4400 self.disallowedNonMethodNames = [] 4401 4402 def isMaplike(self): 4403 return self.maplikeOrSetlikeOrIterableType == "maplike" 4404 4405 def isSetlike(self): 4406 return self.maplikeOrSetlikeOrIterableType == "setlike" 4407 4408 def isIterable(self): 4409 return self.maplikeOrSetlikeOrIterableType == "iterable" 4410 4411 def hasKeyType(self): 4412 return self.keyType is not None 4413 4414 def hasValueType(self): 4415 return self.valueType is not None 4416 4417 def checkCollisions(self, members, isAncestor): 4418 for member in members: 4419 # Check that there are no disallowed members 4420 if member.identifier.name in self.disallowedMemberNames and not ( 4421 (member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) 4422 or (member.isAttr() and member.isMaplikeOrSetlikeAttr()) 4423 ): 4424 raise WebIDLError( 4425 "Member '%s' conflicts " 4426 "with reserved %s name." 4427 % (member.identifier.name, self.maplikeOrSetlikeOrIterableType), 4428 [self.location, member.location], 4429 ) 4430 # Check that there are no disallowed non-method members. 4431 # Ancestor members are always disallowed here; own members 4432 # are disallowed only if they're non-methods. 4433 if ( 4434 isAncestor or member.isAttr() or member.isConst() 4435 ) and member.identifier.name in self.disallowedNonMethodNames: 4436 raise WebIDLError( 4437 "Member '%s' conflicts " 4438 "with reserved %s method." 4439 % (member.identifier.name, self.maplikeOrSetlikeOrIterableType), 4440 [self.location, member.location], 4441 ) 4442 4443 def addMethod( 4444 self, 4445 name, 4446 members, 4447 allowExistingOperations, 4448 returnType, 4449 args=[], 4450 chromeOnly=False, 4451 isPure=False, 4452 affectsNothing=False, 4453 newObject=False, 4454 isIteratorAlias=False, 4455 ): 4456 """ 4457 Create an IDLMethod based on the parameters passed in. 4458 4459 - members is the member list to add this function to, since this is 4460 called during the member expansion portion of interface object 4461 building. 4462 4463 - chromeOnly is only True for read-only js implemented classes, to 4464 implement underscore prefixed convenience functions which would 4465 otherwise not be available, unlike the case of C++ bindings. 4466 4467 - isPure is only True for idempotent functions, so it is not valid for 4468 things like keys, values, etc. that return a new object every time. 4469 4470 - affectsNothing means that nothing changes due to this method, which 4471 affects JIT optimization behavior 4472 4473 - newObject means the method creates and returns a new object. 4474 4475 """ 4476 # Only add name to lists for collision checks if it's not chrome 4477 # only. 4478 if chromeOnly: 4479 name = "__" + name 4480 else: 4481 if not allowExistingOperations: 4482 self.disallowedMemberNames.append(name) 4483 else: 4484 self.disallowedNonMethodNames.append(name) 4485 # If allowExistingOperations is True, and another operation exists 4486 # with the same name as the one we're trying to add, don't add the 4487 # maplike/setlike operation. However, if the operation is static, 4488 # then fail by way of creating the function, which will cause a 4489 # naming conflict, per the spec. 4490 if allowExistingOperations: 4491 for m in members: 4492 if m.identifier.name == name and m.isMethod() and not m.isStatic(): 4493 return 4494 method = IDLMethod( 4495 self.location, 4496 IDLUnresolvedIdentifier( 4497 self.location, name, allowDoubleUnderscore=chromeOnly 4498 ), 4499 returnType, 4500 args, 4501 maplikeOrSetlikeOrIterable=self, 4502 ) 4503 # We need to be able to throw from declaration methods 4504 method.addExtendedAttributes([IDLExtendedAttribute(self.location, ("Throws",))]) 4505 if chromeOnly: 4506 method.addExtendedAttributes( 4507 [IDLExtendedAttribute(self.location, ("ChromeOnly",))] 4508 ) 4509 if isPure: 4510 method.addExtendedAttributes( 4511 [IDLExtendedAttribute(self.location, ("Pure",))] 4512 ) 4513 # Following attributes are used for keys/values/entries. Can't mark 4514 # them pure, since they return a new object each time they are run. 4515 if affectsNothing: 4516 method.addExtendedAttributes( 4517 [ 4518 IDLExtendedAttribute(self.location, ("DependsOn", "Everything")), 4519 IDLExtendedAttribute(self.location, ("Affects", "Nothing")), 4520 ] 4521 ) 4522 if newObject: 4523 method.addExtendedAttributes( 4524 [IDLExtendedAttribute(self.location, ("NewObject",))] 4525 ) 4526 if isIteratorAlias: 4527 method.addExtendedAttributes( 4528 [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))] 4529 ) 4530 # Methods generated for iterables should be enumerable, but the ones for 4531 # maplike/setlike should not be. 4532 if not self.isIterable(): 4533 method.addExtendedAttributes( 4534 [IDLExtendedAttribute(self.location, ("NonEnumerable",))] 4535 ) 4536 members.append(method) 4537 4538 def resolve(self, parentScope): 4539 if self.keyType: 4540 self.keyType.resolveType(parentScope) 4541 if self.valueType: 4542 self.valueType.resolveType(parentScope) 4543 4544 def finish(self, scope): 4545 IDLInterfaceMember.finish(self, scope) 4546 if self.keyType and not self.keyType.isComplete(): 4547 t = self.keyType.complete(scope) 4548 4549 assert not isinstance(t, IDLUnresolvedType) 4550 assert not isinstance(t, IDLTypedefType) 4551 assert not isinstance(t.name, IDLUnresolvedIdentifier) 4552 self.keyType = t 4553 if self.valueType and not self.valueType.isComplete(): 4554 t = self.valueType.complete(scope) 4555 4556 assert not isinstance(t, IDLUnresolvedType) 4557 assert not isinstance(t, IDLTypedefType) 4558 assert not isinstance(t.name, IDLUnresolvedIdentifier) 4559 self.valueType = t 4560 4561 def validate(self): 4562 IDLInterfaceMember.validate(self) 4563 4564 def handleExtendedAttribute(self, attr): 4565 IDLInterfaceMember.handleExtendedAttribute(self, attr) 4566 4567 def _getDependentObjects(self): 4568 deps = set() 4569 if self.keyType: 4570 deps.add(self.keyType) 4571 if self.valueType: 4572 deps.add(self.valueType) 4573 return deps 4574 4575 def getForEachArguments(self): 4576 return [ 4577 IDLArgument( 4578 self.location, 4579 IDLUnresolvedIdentifier( 4580 BuiltinLocation("<auto-generated-identifier>"), "callback" 4581 ), 4582 BuiltinTypes[IDLBuiltinType.Types.object], 4583 ), 4584 IDLArgument( 4585 self.location, 4586 IDLUnresolvedIdentifier( 4587 BuiltinLocation("<auto-generated-identifier>"), "thisArg" 4588 ), 4589 BuiltinTypes[IDLBuiltinType.Types.any], 4590 optional=True, 4591 ), 4592 ] 4593 4594 4595# Iterable adds ES6 iterator style functions and traits 4596# (keys/values/entries/@@iterator) to an interface. 4597class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase): 4598 def __init__(self, location, identifier, keyType, valueType=None, scope=None): 4599 IDLMaplikeOrSetlikeOrIterableBase.__init__( 4600 self, 4601 location, 4602 identifier, 4603 "iterable", 4604 keyType, 4605 valueType, 4606 IDLInterfaceMember.Tags.Iterable, 4607 ) 4608 self.iteratorType = None 4609 4610 def __str__(self): 4611 return "declared iterable with key '%s' and value '%s'" % ( 4612 self.keyType, 4613 self.valueType, 4614 ) 4615 4616 def expand(self, members): 4617 """ 4618 In order to take advantage of all of the method machinery in Codegen, 4619 we generate our functions as if they were part of the interface 4620 specification during parsing. 4621 """ 4622 # We only need to add entries/keys/values here if we're a pair iterator. 4623 # Value iterators just copy these from %ArrayPrototype% instead. 4624 if not self.isPairIterator(): 4625 return 4626 4627 # object entries() 4628 self.addMethod( 4629 "entries", 4630 members, 4631 False, 4632 self.iteratorType, 4633 affectsNothing=True, 4634 newObject=True, 4635 isIteratorAlias=True, 4636 ) 4637 # object keys() 4638 self.addMethod( 4639 "keys", 4640 members, 4641 False, 4642 self.iteratorType, 4643 affectsNothing=True, 4644 newObject=True, 4645 ) 4646 # object values() 4647 self.addMethod( 4648 "values", 4649 members, 4650 False, 4651 self.iteratorType, 4652 affectsNothing=True, 4653 newObject=True, 4654 ) 4655 4656 # void forEach(callback(valueType, keyType), optional any thisArg) 4657 self.addMethod( 4658 "forEach", 4659 members, 4660 False, 4661 BuiltinTypes[IDLBuiltinType.Types.void], 4662 self.getForEachArguments(), 4663 ) 4664 4665 def isValueIterator(self): 4666 return not self.isPairIterator() 4667 4668 def isPairIterator(self): 4669 return self.hasKeyType() 4670 4671 4672# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface. 4673class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): 4674 def __init__( 4675 self, location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType 4676 ): 4677 IDLMaplikeOrSetlikeOrIterableBase.__init__( 4678 self, 4679 location, 4680 identifier, 4681 maplikeOrSetlikeType, 4682 keyType, 4683 valueType, 4684 IDLInterfaceMember.Tags.MaplikeOrSetlike, 4685 ) 4686 self.readonly = readonly 4687 self.slotIndices = None 4688 4689 # When generating JSAPI access code, we need to know the backing object 4690 # type prefix to create the correct function. Generate here for reuse. 4691 if self.isMaplike(): 4692 self.prefix = "Map" 4693 elif self.isSetlike(): 4694 self.prefix = "Set" 4695 4696 def __str__(self): 4697 return "declared '%s' with key '%s'" % ( 4698 self.maplikeOrSetlikeOrIterableType, 4699 self.keyType, 4700 ) 4701 4702 def expand(self, members): 4703 """ 4704 In order to take advantage of all of the method machinery in Codegen, 4705 we generate our functions as if they were part of the interface 4706 specification during parsing. 4707 """ 4708 # Both maplike and setlike have a size attribute 4709 sizeAttr = IDLAttribute( 4710 self.location, 4711 IDLUnresolvedIdentifier( 4712 BuiltinLocation("<auto-generated-identifier>"), "size" 4713 ), 4714 BuiltinTypes[IDLBuiltinType.Types.unsigned_long], 4715 True, 4716 maplikeOrSetlike=self, 4717 ) 4718 # This should be non-enumerable. 4719 sizeAttr.addExtendedAttributes( 4720 [IDLExtendedAttribute(self.location, ("NonEnumerable",))] 4721 ) 4722 members.append(sizeAttr) 4723 self.reserved_ro_names = ["size"] 4724 self.disallowedMemberNames.append("size") 4725 4726 # object entries() 4727 self.addMethod( 4728 "entries", 4729 members, 4730 False, 4731 BuiltinTypes[IDLBuiltinType.Types.object], 4732 affectsNothing=True, 4733 isIteratorAlias=self.isMaplike(), 4734 ) 4735 # object keys() 4736 self.addMethod( 4737 "keys", 4738 members, 4739 False, 4740 BuiltinTypes[IDLBuiltinType.Types.object], 4741 affectsNothing=True, 4742 ) 4743 # object values() 4744 self.addMethod( 4745 "values", 4746 members, 4747 False, 4748 BuiltinTypes[IDLBuiltinType.Types.object], 4749 affectsNothing=True, 4750 isIteratorAlias=self.isSetlike(), 4751 ) 4752 4753 # void forEach(callback(valueType, keyType), thisVal) 4754 self.addMethod( 4755 "forEach", 4756 members, 4757 False, 4758 BuiltinTypes[IDLBuiltinType.Types.void], 4759 self.getForEachArguments(), 4760 ) 4761 4762 def getKeyArg(): 4763 return IDLArgument( 4764 self.location, 4765 IDLUnresolvedIdentifier(self.location, "key"), 4766 self.keyType, 4767 ) 4768 4769 # boolean has(keyType key) 4770 self.addMethod( 4771 "has", 4772 members, 4773 False, 4774 BuiltinTypes[IDLBuiltinType.Types.boolean], 4775 [getKeyArg()], 4776 isPure=True, 4777 ) 4778 4779 if not self.readonly: 4780 # void clear() 4781 self.addMethod( 4782 "clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void], [] 4783 ) 4784 # boolean delete(keyType key) 4785 self.addMethod( 4786 "delete", 4787 members, 4788 True, 4789 BuiltinTypes[IDLBuiltinType.Types.boolean], 4790 [getKeyArg()], 4791 ) 4792 4793 if self.isSetlike(): 4794 if not self.readonly: 4795 # Add returns the set object it just added to. 4796 # object add(keyType key) 4797 4798 self.addMethod( 4799 "add", 4800 members, 4801 True, 4802 BuiltinTypes[IDLBuiltinType.Types.object], 4803 [getKeyArg()], 4804 ) 4805 return 4806 4807 # If we get this far, we're a maplike declaration. 4808 4809 # valueType get(keyType key) 4810 # 4811 # Note that instead of the value type, we're using any here. The 4812 # validity checks should happen as things are inserted into the map, 4813 # and using any as the return type makes code generation much simpler. 4814 # 4815 # TODO: Bug 1155340 may change this to use specific type to provide 4816 # more info to JIT. 4817 self.addMethod( 4818 "get", 4819 members, 4820 False, 4821 BuiltinTypes[IDLBuiltinType.Types.any], 4822 [getKeyArg()], 4823 isPure=True, 4824 ) 4825 4826 def getValueArg(): 4827 return IDLArgument( 4828 self.location, 4829 IDLUnresolvedIdentifier(self.location, "value"), 4830 self.valueType, 4831 ) 4832 4833 if not self.readonly: 4834 self.addMethod( 4835 "set", 4836 members, 4837 True, 4838 BuiltinTypes[IDLBuiltinType.Types.object], 4839 [getKeyArg(), getValueArg()], 4840 ) 4841 4842 4843class IDLConst(IDLInterfaceMember): 4844 def __init__(self, location, identifier, type, value): 4845 IDLInterfaceMember.__init__( 4846 self, location, identifier, IDLInterfaceMember.Tags.Const 4847 ) 4848 4849 assert isinstance(type, IDLType) 4850 if type.isDictionary(): 4851 raise WebIDLError( 4852 "A constant cannot be of a dictionary type", [self.location] 4853 ) 4854 if type.isRecord(): 4855 raise WebIDLError("A constant cannot be of a record type", [self.location]) 4856 self.type = type 4857 self.value = value 4858 4859 if identifier.name == "prototype": 4860 raise WebIDLError( 4861 "The identifier of a constant must not be 'prototype'", [location] 4862 ) 4863 4864 def __str__(self): 4865 return "'%s' const '%s'" % (self.type, self.identifier) 4866 4867 def finish(self, scope): 4868 IDLInterfaceMember.finish(self, scope) 4869 4870 if not self.type.isComplete(): 4871 type = self.type.complete(scope) 4872 if not type.isPrimitive() and not type.isString(): 4873 locations = [self.type.location, type.location] 4874 try: 4875 locations.append(type.inner.location) 4876 except: 4877 pass 4878 raise WebIDLError("Incorrect type for constant", locations) 4879 self.type = type 4880 4881 # The value might not match the type 4882 coercedValue = self.value.coerceToType(self.type, self.location) 4883 assert coercedValue 4884 4885 self.value = coercedValue 4886 4887 def validate(self): 4888 IDLInterfaceMember.validate(self) 4889 4890 def handleExtendedAttribute(self, attr): 4891 identifier = attr.identifier() 4892 if identifier == "Exposed": 4893 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 4894 elif ( 4895 identifier == "Pref" 4896 or identifier == "ChromeOnly" 4897 or identifier == "Func" 4898 or identifier == "SecureContext" 4899 or identifier == "NonEnumerable" 4900 ): 4901 # Known attributes that we don't need to do anything with here 4902 pass 4903 else: 4904 raise WebIDLError( 4905 "Unknown extended attribute %s on constant" % identifier, 4906 [attr.location], 4907 ) 4908 IDLInterfaceMember.handleExtendedAttribute(self, attr) 4909 4910 def _getDependentObjects(self): 4911 return set([self.type, self.value]) 4912 4913 4914class IDLAttribute(IDLInterfaceMember): 4915 def __init__( 4916 self, 4917 location, 4918 identifier, 4919 type, 4920 readonly, 4921 inherit=False, 4922 static=False, 4923 stringifier=False, 4924 maplikeOrSetlike=None, 4925 extendedAttrDict=None, 4926 ): 4927 IDLInterfaceMember.__init__( 4928 self, 4929 location, 4930 identifier, 4931 IDLInterfaceMember.Tags.Attr, 4932 extendedAttrDict=extendedAttrDict, 4933 ) 4934 4935 assert isinstance(type, IDLType) 4936 self.type = type 4937 self.readonly = readonly 4938 self.inherit = inherit 4939 self._static = static 4940 self.legacyLenientThis = False 4941 self._legacyUnforgeable = False 4942 self.stringifier = stringifier 4943 self.slotIndices = None 4944 assert maplikeOrSetlike is None or isinstance( 4945 maplikeOrSetlike, IDLMaplikeOrSetlike 4946 ) 4947 self.maplikeOrSetlike = maplikeOrSetlike 4948 self.dependsOn = "Everything" 4949 self.affects = "Everything" 4950 self.bindingAliases = [] 4951 4952 if static and identifier.name == "prototype": 4953 raise WebIDLError( 4954 "The identifier of a static attribute must not be 'prototype'", 4955 [location], 4956 ) 4957 4958 if readonly and inherit: 4959 raise WebIDLError( 4960 "An attribute cannot be both 'readonly' and 'inherit'", [self.location] 4961 ) 4962 4963 def isStatic(self): 4964 return self._static 4965 4966 def forceStatic(self): 4967 self._static = True 4968 4969 def __str__(self): 4970 return "'%s' attribute '%s'" % (self.type, self.identifier) 4971 4972 def finish(self, scope): 4973 IDLInterfaceMember.finish(self, scope) 4974 4975 if not self.type.isComplete(): 4976 t = self.type.complete(scope) 4977 4978 assert not isinstance(t, IDLUnresolvedType) 4979 assert not isinstance(t, IDLTypedefType) 4980 assert not isinstance(t.name, IDLUnresolvedIdentifier) 4981 self.type = t 4982 4983 if self.readonly and ( 4984 self.type.hasClamp() 4985 or self.type.hasEnforceRange() 4986 or self.type.hasAllowShared() 4987 or self.type.legacyNullToEmptyString 4988 ): 4989 raise WebIDLError( 4990 "A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]", 4991 [self.location], 4992 ) 4993 if self.type.isDictionary() and not self.getExtendedAttribute("Cached"): 4994 raise WebIDLError( 4995 "An attribute cannot be of a dictionary type", [self.location] 4996 ) 4997 if self.type.isSequence() and not self.getExtendedAttribute("Cached"): 4998 raise WebIDLError( 4999 "A non-cached attribute cannot be of a sequence " "type", 5000 [self.location], 5001 ) 5002 if self.type.isRecord() and not self.getExtendedAttribute("Cached"): 5003 raise WebIDLError( 5004 "A non-cached attribute cannot be of a record " "type", [self.location] 5005 ) 5006 if self.type.isUnion(): 5007 for f in self.type.unroll().flatMemberTypes: 5008 if f.isDictionary(): 5009 raise WebIDLError( 5010 "An attribute cannot be of a union " 5011 "type if one of its member types (or " 5012 "one of its member types's member " 5013 "types, and so on) is a dictionary " 5014 "type", 5015 [self.location, f.location], 5016 ) 5017 if f.isSequence(): 5018 raise WebIDLError( 5019 "An attribute cannot be of a union " 5020 "type if one of its member types (or " 5021 "one of its member types's member " 5022 "types, and so on) is a sequence " 5023 "type", 5024 [self.location, f.location], 5025 ) 5026 if f.isRecord(): 5027 raise WebIDLError( 5028 "An attribute cannot be of a union " 5029 "type if one of its member types (or " 5030 "one of its member types's member " 5031 "types, and so on) is a record " 5032 "type", 5033 [self.location, f.location], 5034 ) 5035 if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"): 5036 raise WebIDLError( 5037 "An attribute with [PutForwards] must have an " 5038 "interface type as its type", 5039 [self.location], 5040 ) 5041 5042 if not self.type.isInterface() and self.getExtendedAttribute("SameObject"): 5043 raise WebIDLError( 5044 "An attribute with [SameObject] must have an " 5045 "interface type as its type", 5046 [self.location], 5047 ) 5048 5049 if self.type.isPromise() and not self.readonly: 5050 raise WebIDLError( 5051 "Promise-returning attributes must be readonly", [self.location] 5052 ) 5053 5054 def validate(self): 5055 def typeContainsChromeOnlyDictionaryMember(type): 5056 if type.nullable() or type.isSequence() or type.isRecord(): 5057 return typeContainsChromeOnlyDictionaryMember(type.inner) 5058 5059 if type.isUnion(): 5060 for memberType in type.flatMemberTypes: 5061 (contains, location) = typeContainsChromeOnlyDictionaryMember( 5062 memberType 5063 ) 5064 if contains: 5065 return (True, location) 5066 5067 if type.isDictionary(): 5068 dictionary = type.inner 5069 while dictionary: 5070 (contains, location) = dictionaryContainsChromeOnlyMember( 5071 dictionary 5072 ) 5073 if contains: 5074 return (True, location) 5075 dictionary = dictionary.parent 5076 5077 return (False, None) 5078 5079 def dictionaryContainsChromeOnlyMember(dictionary): 5080 for member in dictionary.members: 5081 if member.getExtendedAttribute("ChromeOnly"): 5082 return (True, member.location) 5083 (contains, location) = typeContainsChromeOnlyDictionaryMember( 5084 member.type 5085 ) 5086 if contains: 5087 return (True, location) 5088 return (False, None) 5089 5090 IDLInterfaceMember.validate(self) 5091 5092 if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( 5093 "StoreInSlot" 5094 ): 5095 if not self.affects == "Nothing": 5096 raise WebIDLError( 5097 "Cached attributes and attributes stored in " 5098 "slots must be Constant or Pure or " 5099 "Affects=Nothing, since the getter won't always " 5100 "be called.", 5101 [self.location], 5102 ) 5103 (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type) 5104 if contains: 5105 raise WebIDLError( 5106 "[Cached] and [StoreInSlot] must not be used " 5107 "on an attribute whose type contains a " 5108 "[ChromeOnly] dictionary member", 5109 [self.location, location], 5110 ) 5111 if self.getExtendedAttribute("Frozen"): 5112 if ( 5113 not self.type.isSequence() 5114 and not self.type.isDictionary() 5115 and not self.type.isRecord() 5116 ): 5117 raise WebIDLError( 5118 "[Frozen] is only allowed on " 5119 "sequence-valued, dictionary-valued, and " 5120 "record-valued attributes", 5121 [self.location], 5122 ) 5123 if not self.type.unroll().isExposedInAllOf(self.exposureSet): 5124 raise WebIDLError( 5125 "Attribute returns a type that is not exposed " 5126 "everywhere where the attribute is exposed", 5127 [self.location], 5128 ) 5129 if self.getExtendedAttribute("CEReactions"): 5130 if self.readonly: 5131 raise WebIDLError( 5132 "[CEReactions] is not allowed on " "readonly attributes", 5133 [self.location], 5134 ) 5135 5136 def handleExtendedAttribute(self, attr): 5137 identifier = attr.identifier() 5138 if ( 5139 identifier == "SetterThrows" 5140 or identifier == "SetterCanOOM" 5141 or identifier == "SetterNeedsSubjectPrincipal" 5142 ) and self.readonly: 5143 raise WebIDLError( 5144 "Readonly attributes must not be flagged as " "[%s]" % identifier, 5145 [self.location], 5146 ) 5147 elif identifier == "BindingAlias": 5148 if not attr.hasValue(): 5149 raise WebIDLError( 5150 "[BindingAlias] takes an identifier or string", [attr.location] 5151 ) 5152 self._addBindingAlias(attr.value()) 5153 elif ( 5154 ( 5155 identifier == "Throws" 5156 or identifier == "GetterThrows" 5157 or identifier == "CanOOM" 5158 or identifier == "GetterCanOOM" 5159 ) 5160 and self.getExtendedAttribute("StoreInSlot") 5161 ) or ( 5162 identifier == "StoreInSlot" 5163 and ( 5164 self.getExtendedAttribute("Throws") 5165 or self.getExtendedAttribute("GetterThrows") 5166 or self.getExtendedAttribute("CanOOM") 5167 or self.getExtendedAttribute("GetterCanOOM") 5168 ) 5169 ): 5170 raise WebIDLError("Throwing things can't be [StoreInSlot]", [attr.location]) 5171 elif identifier == "LegacyLenientThis": 5172 if not attr.noArguments(): 5173 raise WebIDLError( 5174 "[LegacyLenientThis] must take no arguments", [attr.location] 5175 ) 5176 if self.isStatic(): 5177 raise WebIDLError( 5178 "[LegacyLenientThis] is only allowed on non-static " "attributes", 5179 [attr.location, self.location], 5180 ) 5181 if self.getExtendedAttribute("CrossOriginReadable"): 5182 raise WebIDLError( 5183 "[LegacyLenientThis] is not allowed in combination " 5184 "with [CrossOriginReadable]", 5185 [attr.location, self.location], 5186 ) 5187 if self.getExtendedAttribute("CrossOriginWritable"): 5188 raise WebIDLError( 5189 "[LegacyLenientThis] is not allowed in combination " 5190 "with [CrossOriginWritable]", 5191 [attr.location, self.location], 5192 ) 5193 self.legacyLenientThis = True 5194 elif identifier == "LegacyUnforgeable": 5195 if self.isStatic(): 5196 raise WebIDLError( 5197 "[LegacyUnforgeable] is only allowed on non-static " "attributes", 5198 [attr.location, self.location], 5199 ) 5200 self._legacyUnforgeable = True 5201 elif identifier == "SameObject" and not self.readonly: 5202 raise WebIDLError( 5203 "[SameObject] only allowed on readonly attributes", 5204 [attr.location, self.location], 5205 ) 5206 elif identifier == "Constant" and not self.readonly: 5207 raise WebIDLError( 5208 "[Constant] only allowed on readonly attributes", 5209 [attr.location, self.location], 5210 ) 5211 elif identifier == "PutForwards": 5212 if not self.readonly: 5213 raise WebIDLError( 5214 "[PutForwards] is only allowed on readonly " "attributes", 5215 [attr.location, self.location], 5216 ) 5217 if self.type.isPromise(): 5218 raise WebIDLError( 5219 "[PutForwards] is not allowed on " "Promise-typed attributes", 5220 [attr.location, self.location], 5221 ) 5222 if self.isStatic(): 5223 raise WebIDLError( 5224 "[PutForwards] is only allowed on non-static " "attributes", 5225 [attr.location, self.location], 5226 ) 5227 if self.getExtendedAttribute("Replaceable") is not None: 5228 raise WebIDLError( 5229 "[PutForwards] and [Replaceable] can't both " 5230 "appear on the same attribute", 5231 [attr.location, self.location], 5232 ) 5233 if not attr.hasValue(): 5234 raise WebIDLError( 5235 "[PutForwards] takes an identifier", [attr.location, self.location] 5236 ) 5237 elif identifier == "Replaceable": 5238 if not attr.noArguments(): 5239 raise WebIDLError( 5240 "[Replaceable] must take no arguments", [attr.location] 5241 ) 5242 if not self.readonly: 5243 raise WebIDLError( 5244 "[Replaceable] is only allowed on readonly " "attributes", 5245 [attr.location, self.location], 5246 ) 5247 if self.type.isPromise(): 5248 raise WebIDLError( 5249 "[Replaceable] is not allowed on " "Promise-typed attributes", 5250 [attr.location, self.location], 5251 ) 5252 if self.isStatic(): 5253 raise WebIDLError( 5254 "[Replaceable] is only allowed on non-static " "attributes", 5255 [attr.location, self.location], 5256 ) 5257 if self.getExtendedAttribute("PutForwards") is not None: 5258 raise WebIDLError( 5259 "[PutForwards] and [Replaceable] can't both " 5260 "appear on the same attribute", 5261 [attr.location, self.location], 5262 ) 5263 elif identifier == "LegacyLenientSetter": 5264 if not attr.noArguments(): 5265 raise WebIDLError( 5266 "[LegacyLenientSetter] must take no arguments", [attr.location] 5267 ) 5268 if not self.readonly: 5269 raise WebIDLError( 5270 "[LegacyLenientSetter] is only allowed on readonly " "attributes", 5271 [attr.location, self.location], 5272 ) 5273 if self.type.isPromise(): 5274 raise WebIDLError( 5275 "[LegacyLenientSetter] is not allowed on " 5276 "Promise-typed attributes", 5277 [attr.location, self.location], 5278 ) 5279 if self.isStatic(): 5280 raise WebIDLError( 5281 "[LegacyLenientSetter] is only allowed on non-static " "attributes", 5282 [attr.location, self.location], 5283 ) 5284 if self.getExtendedAttribute("PutForwards") is not None: 5285 raise WebIDLError( 5286 "[LegacyLenientSetter] and [PutForwards] can't both " 5287 "appear on the same attribute", 5288 [attr.location, self.location], 5289 ) 5290 if self.getExtendedAttribute("Replaceable") is not None: 5291 raise WebIDLError( 5292 "[LegacyLenientSetter] and [Replaceable] can't both " 5293 "appear on the same attribute", 5294 [attr.location, self.location], 5295 ) 5296 elif identifier == "LenientFloat": 5297 if self.readonly: 5298 raise WebIDLError( 5299 "[LenientFloat] used on a readonly attribute", 5300 [attr.location, self.location], 5301 ) 5302 if not self.type.includesRestrictedFloat(): 5303 raise WebIDLError( 5304 "[LenientFloat] used on an attribute with a " 5305 "non-restricted-float type", 5306 [attr.location, self.location], 5307 ) 5308 elif identifier == "StoreInSlot": 5309 if self.getExtendedAttribute("Cached"): 5310 raise WebIDLError( 5311 "[StoreInSlot] and [Cached] must not be " 5312 "specified on the same attribute", 5313 [attr.location, self.location], 5314 ) 5315 elif identifier == "Cached": 5316 if self.getExtendedAttribute("StoreInSlot"): 5317 raise WebIDLError( 5318 "[Cached] and [StoreInSlot] must not be " 5319 "specified on the same attribute", 5320 [attr.location, self.location], 5321 ) 5322 elif identifier == "CrossOriginReadable" or identifier == "CrossOriginWritable": 5323 if not attr.noArguments(): 5324 raise WebIDLError( 5325 "[%s] must take no arguments" % identifier, [attr.location] 5326 ) 5327 if self.isStatic(): 5328 raise WebIDLError( 5329 "[%s] is only allowed on non-static " "attributes" % identifier, 5330 [attr.location, self.location], 5331 ) 5332 if self.getExtendedAttribute("LegacyLenientThis"): 5333 raise WebIDLError( 5334 "[LegacyLenientThis] is not allowed in combination " 5335 "with [%s]" % identifier, 5336 [attr.location, self.location], 5337 ) 5338 elif identifier == "Exposed": 5339 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 5340 elif identifier == "Pure": 5341 if not attr.noArguments(): 5342 raise WebIDLError("[Pure] must take no arguments", [attr.location]) 5343 self._setDependsOn("DOMState") 5344 self._setAffects("Nothing") 5345 elif identifier == "Constant" or identifier == "SameObject": 5346 if not attr.noArguments(): 5347 raise WebIDLError( 5348 "[%s] must take no arguments" % identifier, [attr.location] 5349 ) 5350 self._setDependsOn("Nothing") 5351 self._setAffects("Nothing") 5352 elif identifier == "Affects": 5353 if not attr.hasValue(): 5354 raise WebIDLError("[Affects] takes an identifier", [attr.location]) 5355 self._setAffects(attr.value()) 5356 elif identifier == "DependsOn": 5357 if not attr.hasValue(): 5358 raise WebIDLError("[DependsOn] takes an identifier", [attr.location]) 5359 if ( 5360 attr.value() != "Everything" 5361 and attr.value() != "DOMState" 5362 and not self.readonly 5363 ): 5364 raise WebIDLError( 5365 "[DependsOn=%s] only allowed on " 5366 "readonly attributes" % attr.value(), 5367 [attr.location, self.location], 5368 ) 5369 self._setDependsOn(attr.value()) 5370 elif identifier == "UseCounter": 5371 if self.stringifier: 5372 raise WebIDLError( 5373 "[UseCounter] must not be used on a " "stringifier attribute", 5374 [attr.location, self.location], 5375 ) 5376 elif identifier == "Unscopable": 5377 if not attr.noArguments(): 5378 raise WebIDLError( 5379 "[Unscopable] must take no arguments", [attr.location] 5380 ) 5381 if self.isStatic(): 5382 raise WebIDLError( 5383 "[Unscopable] is only allowed on non-static " 5384 "attributes and operations", 5385 [attr.location, self.location], 5386 ) 5387 elif identifier == "CEReactions": 5388 if not attr.noArguments(): 5389 raise WebIDLError( 5390 "[CEReactions] must take no arguments", [attr.location] 5391 ) 5392 elif ( 5393 identifier == "Pref" 5394 or identifier == "Deprecated" 5395 or identifier == "SetterThrows" 5396 or identifier == "Throws" 5397 or identifier == "GetterThrows" 5398 or identifier == "SetterCanOOM" 5399 or identifier == "CanOOM" 5400 or identifier == "GetterCanOOM" 5401 or identifier == "ChromeOnly" 5402 or identifier == "Func" 5403 or identifier == "SecureContext" 5404 or identifier == "Frozen" 5405 or identifier == "NewObject" 5406 or identifier == "NeedsSubjectPrincipal" 5407 or identifier == "SetterNeedsSubjectPrincipal" 5408 or identifier == "GetterNeedsSubjectPrincipal" 5409 or identifier == "NeedsCallerType" 5410 or identifier == "ReturnValueNeedsContainsHack" 5411 or identifier == "BinaryName" 5412 or identifier == "NonEnumerable" 5413 ): 5414 # Known attributes that we don't need to do anything with here 5415 pass 5416 else: 5417 raise WebIDLError( 5418 "Unknown extended attribute %s on attribute" % identifier, 5419 [attr.location], 5420 ) 5421 IDLInterfaceMember.handleExtendedAttribute(self, attr) 5422 5423 def resolve(self, parentScope): 5424 assert isinstance(parentScope, IDLScope) 5425 self.type.resolveType(parentScope) 5426 IDLObjectWithIdentifier.resolve(self, parentScope) 5427 5428 def hasLegacyLenientThis(self): 5429 return self.legacyLenientThis 5430 5431 def isMaplikeOrSetlikeAttr(self): 5432 """ 5433 True if this attribute was generated from an interface with 5434 maplike/setlike (e.g. this is the size attribute for 5435 maplike/setlike) 5436 """ 5437 return self.maplikeOrSetlike is not None 5438 5439 def isLegacyUnforgeable(self): 5440 return self._legacyUnforgeable 5441 5442 def _getDependentObjects(self): 5443 return set([self.type]) 5444 5445 def expand(self, members): 5446 assert self.stringifier 5447 if ( 5448 not self.type.isDOMString() 5449 and not self.type.isUSVString() 5450 and not self.type.isUTF8String() 5451 ): 5452 raise WebIDLError( 5453 "The type of a stringifer attribute must be " 5454 "either DOMString, USVString or UTF8String", 5455 [self.location], 5456 ) 5457 identifier = IDLUnresolvedIdentifier( 5458 self.location, "__stringifier", allowDoubleUnderscore=True 5459 ) 5460 method = IDLMethod( 5461 self.location, 5462 identifier, 5463 returnType=self.type, 5464 arguments=[], 5465 stringifier=True, 5466 underlyingAttr=self, 5467 ) 5468 allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"] 5469 # Safe to ignore these as they are only meaningful for attributes 5470 attributeOnlyExtAttrs = [ 5471 "CEReactions", 5472 "CrossOriginWritable", 5473 "SetterThrows", 5474 ] 5475 for (key, value) in self._extendedAttrDict.items(): 5476 if key in allowedExtAttrs: 5477 if value is not True: 5478 raise WebIDLError( 5479 "[%s] with a value is currently " 5480 "unsupported in stringifier attributes, " 5481 "please file a bug to add support" % key, 5482 [self.location], 5483 ) 5484 method.addExtendedAttributes( 5485 [IDLExtendedAttribute(self.location, (key,))] 5486 ) 5487 elif not key in attributeOnlyExtAttrs: 5488 raise WebIDLError( 5489 "[%s] is currently unsupported in " 5490 "stringifier attributes, please file a bug " 5491 "to add support" % key, 5492 [self.location], 5493 ) 5494 members.append(method) 5495 5496 5497class IDLArgument(IDLObjectWithIdentifier): 5498 def __init__( 5499 self, 5500 location, 5501 identifier, 5502 type, 5503 optional=False, 5504 defaultValue=None, 5505 variadic=False, 5506 dictionaryMember=False, 5507 allowTypeAttributes=False, 5508 ): 5509 IDLObjectWithIdentifier.__init__(self, location, None, identifier) 5510 5511 assert isinstance(type, IDLType) 5512 self.type = type 5513 5514 self.optional = optional 5515 self.defaultValue = defaultValue 5516 self.variadic = variadic 5517 self.dictionaryMember = dictionaryMember 5518 self._isComplete = False 5519 self._allowTreatNonCallableAsNull = False 5520 self._extendedAttrDict = {} 5521 self.allowTypeAttributes = allowTypeAttributes 5522 5523 assert not variadic or optional 5524 assert not variadic or not defaultValue 5525 5526 def addExtendedAttributes(self, attrs): 5527 for attribute in attrs: 5528 identifier = attribute.identifier() 5529 if self.allowTypeAttributes and ( 5530 identifier == "EnforceRange" 5531 or identifier == "Clamp" 5532 or identifier == "LegacyNullToEmptyString" 5533 or identifier == "AllowShared" 5534 ): 5535 self.type = self.type.withExtendedAttributes([attribute]) 5536 elif identifier == "TreatNonCallableAsNull": 5537 self._allowTreatNonCallableAsNull = True 5538 elif self.dictionaryMember and ( 5539 identifier == "ChromeOnly" 5540 or identifier == "Func" 5541 or identifier == "Pref" 5542 ): 5543 if not self.optional: 5544 raise WebIDLError( 5545 "[%s] must not be used on a required " 5546 "dictionary member" % identifier, 5547 [attribute.location], 5548 ) 5549 else: 5550 raise WebIDLError( 5551 "Unhandled extended attribute on %s" 5552 % ( 5553 "a dictionary member" 5554 if self.dictionaryMember 5555 else "an argument" 5556 ), 5557 [attribute.location], 5558 ) 5559 attrlist = attribute.listValue() 5560 self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True 5561 5562 def getExtendedAttribute(self, name): 5563 return self._extendedAttrDict.get(name, None) 5564 5565 def isComplete(self): 5566 return self._isComplete 5567 5568 def complete(self, scope): 5569 if self._isComplete: 5570 return 5571 5572 self._isComplete = True 5573 5574 if not self.type.isComplete(): 5575 type = self.type.complete(scope) 5576 assert not isinstance(type, IDLUnresolvedType) 5577 assert not isinstance(type, IDLTypedefType) 5578 assert not isinstance(type.name, IDLUnresolvedIdentifier) 5579 self.type = type 5580 5581 if self.type.isAny(): 5582 assert self.defaultValue is None or isinstance( 5583 self.defaultValue, IDLNullValue 5584 ) 5585 # optional 'any' values always have a default value 5586 if self.optional and not self.defaultValue and not self.variadic: 5587 # Set the default value to undefined, for simplicity, so the 5588 # codegen doesn't have to special-case this. 5589 self.defaultValue = IDLUndefinedValue(self.location) 5590 5591 if self.dictionaryMember and self.type.legacyNullToEmptyString: 5592 raise WebIDLError( 5593 "Dictionary members cannot be [LegacyNullToEmptyString]", 5594 [self.location], 5595 ) 5596 # Now do the coercing thing; this needs to happen after the 5597 # above creation of a default value. 5598 if self.defaultValue: 5599 self.defaultValue = self.defaultValue.coerceToType(self.type, self.location) 5600 assert self.defaultValue 5601 5602 def allowTreatNonCallableAsNull(self): 5603 return self._allowTreatNonCallableAsNull 5604 5605 def _getDependentObjects(self): 5606 deps = set([self.type]) 5607 if self.defaultValue: 5608 deps.add(self.defaultValue) 5609 return deps 5610 5611 def canHaveMissingValue(self): 5612 return self.optional and not self.defaultValue 5613 5614 5615class IDLCallback(IDLObjectWithScope): 5616 def __init__( 5617 self, location, parentScope, identifier, returnType, arguments, isConstructor 5618 ): 5619 assert isinstance(returnType, IDLType) 5620 5621 self._returnType = returnType 5622 # Clone the list 5623 self._arguments = list(arguments) 5624 5625 IDLObjectWithScope.__init__(self, location, parentScope, identifier) 5626 5627 for (returnType, arguments) in self.signatures(): 5628 for argument in arguments: 5629 argument.resolve(self) 5630 5631 self._treatNonCallableAsNull = False 5632 self._treatNonObjectAsNull = False 5633 self._isRunScriptBoundary = False 5634 self._isConstructor = isConstructor 5635 5636 def isCallback(self): 5637 return True 5638 5639 def isConstructor(self): 5640 return self._isConstructor 5641 5642 def signatures(self): 5643 return [(self._returnType, self._arguments)] 5644 5645 def finish(self, scope): 5646 if not self._returnType.isComplete(): 5647 type = self._returnType.complete(scope) 5648 5649 assert not isinstance(type, IDLUnresolvedType) 5650 assert not isinstance(type, IDLTypedefType) 5651 assert not isinstance(type.name, IDLUnresolvedIdentifier) 5652 self._returnType = type 5653 5654 for argument in self._arguments: 5655 if argument.type.isComplete(): 5656 continue 5657 5658 type = argument.type.complete(scope) 5659 5660 assert not isinstance(type, IDLUnresolvedType) 5661 assert not isinstance(type, IDLTypedefType) 5662 assert not isinstance(type.name, IDLUnresolvedIdentifier) 5663 argument.type = type 5664 5665 def validate(self): 5666 pass 5667 5668 def addExtendedAttributes(self, attrs): 5669 unhandledAttrs = [] 5670 for attr in attrs: 5671 if attr.identifier() == "TreatNonCallableAsNull": 5672 self._treatNonCallableAsNull = True 5673 elif attr.identifier() == "LegacyTreatNonObjectAsNull": 5674 if self._isConstructor: 5675 raise WebIDLError( 5676 "[LegacyTreatNonObjectAsNull] is not supported " 5677 "on constructors", 5678 [self.location], 5679 ) 5680 self._treatNonObjectAsNull = True 5681 elif attr.identifier() == "MOZ_CAN_RUN_SCRIPT_BOUNDARY": 5682 if self._isConstructor: 5683 raise WebIDLError( 5684 "[MOZ_CAN_RUN_SCRIPT_BOUNDARY] is not " 5685 "permitted on constructors", 5686 [self.location], 5687 ) 5688 self._isRunScriptBoundary = True 5689 else: 5690 unhandledAttrs.append(attr) 5691 if self._treatNonCallableAsNull and self._treatNonObjectAsNull: 5692 raise WebIDLError( 5693 "Cannot specify both [TreatNonCallableAsNull] " 5694 "and [LegacyTreatNonObjectAsNull]", 5695 [self.location], 5696 ) 5697 if len(unhandledAttrs) != 0: 5698 IDLType.addExtendedAttributes(self, unhandledAttrs) 5699 5700 def _getDependentObjects(self): 5701 return set([self._returnType] + self._arguments) 5702 5703 def isRunScriptBoundary(self): 5704 return self._isRunScriptBoundary 5705 5706 5707class IDLCallbackType(IDLType): 5708 def __init__(self, location, callback): 5709 IDLType.__init__(self, location, callback.identifier.name) 5710 self.callback = callback 5711 5712 def isCallback(self): 5713 return True 5714 5715 def tag(self): 5716 return IDLType.Tags.callback 5717 5718 def isDistinguishableFrom(self, other): 5719 if other.isPromise(): 5720 return False 5721 if other.isUnion(): 5722 # Just forward to the union; it'll deal 5723 return other.isDistinguishableFrom(self) 5724 return ( 5725 other.isPrimitive() 5726 or other.isString() 5727 or other.isEnum() 5728 or other.isNonCallbackInterface() 5729 or other.isSequence() 5730 ) 5731 5732 def _getDependentObjects(self): 5733 return self.callback._getDependentObjects() 5734 5735 5736class IDLMethodOverload: 5737 """ 5738 A class that represents a single overload of a WebIDL method. This is not 5739 quite the same as an element of the "effective overload set" in the spec, 5740 because separate IDLMethodOverloads are not created based on arguments being 5741 optional. Rather, when multiple methods have the same name, there is an 5742 IDLMethodOverload for each one, all hanging off an IDLMethod representing 5743 the full set of overloads. 5744 """ 5745 5746 def __init__(self, returnType, arguments, location): 5747 self.returnType = returnType 5748 # Clone the list of arguments, just in case 5749 self.arguments = list(arguments) 5750 self.location = location 5751 5752 def _getDependentObjects(self): 5753 deps = set(self.arguments) 5754 deps.add(self.returnType) 5755 return deps 5756 5757 def includesRestrictedFloatArgument(self): 5758 return any(arg.type.includesRestrictedFloat() for arg in self.arguments) 5759 5760 5761class IDLMethod(IDLInterfaceMember, IDLScope): 5762 5763 Special = enum( 5764 "Getter", "Setter", "Deleter", "LegacyCaller", base=IDLInterfaceMember.Special 5765 ) 5766 5767 NamedOrIndexed = enum("Neither", "Named", "Indexed") 5768 5769 def __init__( 5770 self, 5771 location, 5772 identifier, 5773 returnType, 5774 arguments, 5775 static=False, 5776 getter=False, 5777 setter=False, 5778 deleter=False, 5779 specialType=NamedOrIndexed.Neither, 5780 legacycaller=False, 5781 stringifier=False, 5782 maplikeOrSetlikeOrIterable=None, 5783 underlyingAttr=None, 5784 ): 5785 # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. 5786 IDLInterfaceMember.__init__( 5787 self, location, identifier, IDLInterfaceMember.Tags.Method 5788 ) 5789 5790 self._hasOverloads = False 5791 5792 assert isinstance(returnType, IDLType) 5793 5794 # self._overloads is a list of IDLMethodOverloads 5795 self._overloads = [IDLMethodOverload(returnType, arguments, location)] 5796 5797 assert isinstance(static, bool) 5798 self._static = static 5799 assert isinstance(getter, bool) 5800 self._getter = getter 5801 assert isinstance(setter, bool) 5802 self._setter = setter 5803 assert isinstance(deleter, bool) 5804 self._deleter = deleter 5805 assert isinstance(legacycaller, bool) 5806 self._legacycaller = legacycaller 5807 assert isinstance(stringifier, bool) 5808 self._stringifier = stringifier 5809 assert maplikeOrSetlikeOrIterable is None or isinstance( 5810 maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase 5811 ) 5812 self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable 5813 self._htmlConstructor = False 5814 self.underlyingAttr = underlyingAttr 5815 self._specialType = specialType 5816 self._legacyUnforgeable = False 5817 self.dependsOn = "Everything" 5818 self.affects = "Everything" 5819 self.aliases = [] 5820 5821 if static and identifier.name == "prototype": 5822 raise WebIDLError( 5823 "The identifier of a static operation must not be 'prototype'", 5824 [location], 5825 ) 5826 5827 self.assertSignatureConstraints() 5828 5829 def __str__(self): 5830 return "Method '%s'" % self.identifier 5831 5832 def assertSignatureConstraints(self): 5833 if self._getter or self._deleter: 5834 assert len(self._overloads) == 1 5835 overload = self._overloads[0] 5836 arguments = overload.arguments 5837 assert len(arguments) == 1 5838 assert ( 5839 arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] 5840 or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] 5841 ) 5842 assert not arguments[0].optional and not arguments[0].variadic 5843 assert not self._getter or not overload.returnType.isVoid() 5844 5845 if self._setter: 5846 assert len(self._overloads) == 1 5847 arguments = self._overloads[0].arguments 5848 assert len(arguments) == 2 5849 assert ( 5850 arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] 5851 or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] 5852 ) 5853 assert not arguments[0].optional and not arguments[0].variadic 5854 assert not arguments[1].optional and not arguments[1].variadic 5855 5856 if self._stringifier: 5857 assert len(self._overloads) == 1 5858 overload = self._overloads[0] 5859 assert len(overload.arguments) == 0 5860 if not self.underlyingAttr: 5861 assert ( 5862 overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] 5863 ) 5864 5865 def isStatic(self): 5866 return self._static 5867 5868 def forceStatic(self): 5869 self._static = True 5870 5871 def isGetter(self): 5872 return self._getter 5873 5874 def isSetter(self): 5875 return self._setter 5876 5877 def isDeleter(self): 5878 return self._deleter 5879 5880 def isNamed(self): 5881 assert ( 5882 self._specialType == IDLMethod.NamedOrIndexed.Named 5883 or self._specialType == IDLMethod.NamedOrIndexed.Indexed 5884 ) 5885 return self._specialType == IDLMethod.NamedOrIndexed.Named 5886 5887 def isIndexed(self): 5888 assert ( 5889 self._specialType == IDLMethod.NamedOrIndexed.Named 5890 or self._specialType == IDLMethod.NamedOrIndexed.Indexed 5891 ) 5892 return self._specialType == IDLMethod.NamedOrIndexed.Indexed 5893 5894 def isLegacycaller(self): 5895 return self._legacycaller 5896 5897 def isStringifier(self): 5898 return self._stringifier 5899 5900 def isToJSON(self): 5901 return self.identifier.name == "toJSON" 5902 5903 def isDefaultToJSON(self): 5904 return self.isToJSON() and self.getExtendedAttribute("Default") 5905 5906 def isMaplikeOrSetlikeOrIterableMethod(self): 5907 """ 5908 True if this method was generated as part of a 5909 maplike/setlike/etc interface (e.g. has/get methods) 5910 """ 5911 return self.maplikeOrSetlikeOrIterable is not None 5912 5913 def isSpecial(self): 5914 return ( 5915 self.isGetter() 5916 or self.isSetter() 5917 or self.isDeleter() 5918 or self.isLegacycaller() 5919 or self.isStringifier() 5920 ) 5921 5922 def isHTMLConstructor(self): 5923 return self._htmlConstructor 5924 5925 def hasOverloads(self): 5926 return self._hasOverloads 5927 5928 def isIdentifierLess(self): 5929 """ 5930 True if the method name started with __, and if the method is not a 5931 maplike/setlike method. Interfaces with maplike/setlike will generate 5932 methods starting with __ for chrome only backing object access in JS 5933 implemented interfaces, so while these functions use what is considered 5934 an non-identifier name, they actually DO have an identifier. 5935 """ 5936 return ( 5937 self.identifier.name[:2] == "__" 5938 and not self.isMaplikeOrSetlikeOrIterableMethod() 5939 ) 5940 5941 def resolve(self, parentScope): 5942 assert isinstance(parentScope, IDLScope) 5943 IDLObjectWithIdentifier.resolve(self, parentScope) 5944 IDLScope.__init__(self, self.location, parentScope, self.identifier) 5945 for (returnType, arguments) in self.signatures(): 5946 for argument in arguments: 5947 argument.resolve(self) 5948 5949 def addOverload(self, method): 5950 assert len(method._overloads) == 1 5951 5952 if self._extendedAttrDict != method._extendedAttrDict: 5953 extendedAttrDiff = set(self._extendedAttrDict.keys()) ^ set( 5954 method._extendedAttrDict.keys() 5955 ) 5956 5957 if extendedAttrDiff == {"LenientFloat"}: 5958 if "LenientFloat" not in self._extendedAttrDict: 5959 for overload in self._overloads: 5960 if overload.includesRestrictedFloatArgument(): 5961 raise WebIDLError( 5962 "Restricted float behavior differs on different " 5963 "overloads of %s" % method.identifier, 5964 [overload.location, method.location], 5965 ) 5966 self._extendedAttrDict["LenientFloat"] = method._extendedAttrDict[ 5967 "LenientFloat" 5968 ] 5969 elif method._overloads[0].includesRestrictedFloatArgument(): 5970 raise WebIDLError( 5971 "Restricted float behavior differs on different " 5972 "overloads of %s" % method.identifier, 5973 [self.location, method.location], 5974 ) 5975 else: 5976 raise WebIDLError( 5977 "Extended attributes differ on different " 5978 "overloads of %s" % method.identifier, 5979 [self.location, method.location], 5980 ) 5981 5982 self._overloads.extend(method._overloads) 5983 5984 self._hasOverloads = True 5985 5986 if self.isStatic() != method.isStatic(): 5987 raise WebIDLError( 5988 "Overloaded identifier %s appears with different values of the 'static' attribute" 5989 % method.identifier, 5990 [method.location], 5991 ) 5992 5993 if self.isLegacycaller() != method.isLegacycaller(): 5994 raise WebIDLError( 5995 "Overloaded identifier %s appears with different values of the 'legacycaller' attribute" 5996 % method.identifier, 5997 [method.location], 5998 ) 5999 6000 # Can't overload special things! 6001 assert not self.isGetter() 6002 assert not method.isGetter() 6003 assert not self.isSetter() 6004 assert not method.isSetter() 6005 assert not self.isDeleter() 6006 assert not method.isDeleter() 6007 assert not self.isStringifier() 6008 assert not method.isStringifier() 6009 assert not self.isHTMLConstructor() 6010 assert not method.isHTMLConstructor() 6011 6012 return self 6013 6014 def signatures(self): 6015 return [ 6016 (overload.returnType, overload.arguments) for overload in self._overloads 6017 ] 6018 6019 def finish(self, scope): 6020 IDLInterfaceMember.finish(self, scope) 6021 6022 for overload in self._overloads: 6023 returnType = overload.returnType 6024 if not returnType.isComplete(): 6025 returnType = returnType.complete(scope) 6026 assert not isinstance(returnType, IDLUnresolvedType) 6027 assert not isinstance(returnType, IDLTypedefType) 6028 assert not isinstance(returnType.name, IDLUnresolvedIdentifier) 6029 overload.returnType = returnType 6030 6031 for argument in overload.arguments: 6032 if not argument.isComplete(): 6033 argument.complete(scope) 6034 assert argument.type.isComplete() 6035 6036 # Now compute various information that will be used by the 6037 # WebIDL overload resolution algorithm. 6038 self.maxArgCount = max(len(s[1]) for s in self.signatures()) 6039 self.allowedArgCounts = [ 6040 i 6041 for i in range(self.maxArgCount + 1) 6042 if len(self.signaturesForArgCount(i)) != 0 6043 ] 6044 6045 def validate(self): 6046 IDLInterfaceMember.validate(self) 6047 6048 # Make sure our overloads are properly distinguishable and don't have 6049 # different argument types before the distinguishing args. 6050 for argCount in self.allowedArgCounts: 6051 possibleOverloads = self.overloadsForArgCount(argCount) 6052 if len(possibleOverloads) == 1: 6053 continue 6054 distinguishingIndex = self.distinguishingIndexForArgCount(argCount) 6055 for idx in range(distinguishingIndex): 6056 firstSigType = possibleOverloads[0].arguments[idx].type 6057 for overload in possibleOverloads[1:]: 6058 if overload.arguments[idx].type != firstSigType: 6059 raise WebIDLError( 6060 "Signatures for method '%s' with %d arguments have " 6061 "different types of arguments at index %d, which " 6062 "is before distinguishing index %d" 6063 % ( 6064 self.identifier.name, 6065 argCount, 6066 idx, 6067 distinguishingIndex, 6068 ), 6069 [self.location, overload.location], 6070 ) 6071 6072 overloadWithPromiseReturnType = None 6073 overloadWithoutPromiseReturnType = None 6074 for overload in self._overloads: 6075 returnType = overload.returnType 6076 if not returnType.unroll().isExposedInAllOf(self.exposureSet): 6077 raise WebIDLError( 6078 "Overload returns a type that is not exposed " 6079 "everywhere where the method is exposed", 6080 [overload.location], 6081 ) 6082 6083 variadicArgument = None 6084 6085 arguments = overload.arguments 6086 for (idx, argument) in enumerate(arguments): 6087 assert argument.type.isComplete() 6088 6089 if ( 6090 argument.type.isDictionary() 6091 and argument.type.unroll().inner.canBeEmpty() 6092 ) or ( 6093 argument.type.isUnion() 6094 and argument.type.unroll().hasPossiblyEmptyDictionaryType() 6095 ): 6096 # Optional dictionaries and unions containing optional 6097 # dictionaries at the end of the list or followed by 6098 # optional arguments must be optional. 6099 if not argument.optional and all( 6100 arg.optional for arg in arguments[idx + 1 :] 6101 ): 6102 raise WebIDLError( 6103 "Dictionary argument without any " 6104 "required fields or union argument " 6105 "containing such dictionary not " 6106 "followed by a required argument " 6107 "must be optional", 6108 [argument.location], 6109 ) 6110 6111 if not argument.defaultValue and all( 6112 arg.optional for arg in arguments[idx + 1 :] 6113 ): 6114 raise WebIDLError( 6115 "Dictionary argument without any " 6116 "required fields or union argument " 6117 "containing such dictionary not " 6118 "followed by a required argument " 6119 "must have a default value", 6120 [argument.location], 6121 ) 6122 6123 # An argument cannot be a nullable dictionary or a 6124 # nullable union containing a dictionary. 6125 if argument.type.nullable() and ( 6126 argument.type.isDictionary() 6127 or ( 6128 argument.type.isUnion() 6129 and argument.type.unroll().hasDictionaryType() 6130 ) 6131 ): 6132 raise WebIDLError( 6133 "An argument cannot be a nullable " 6134 "dictionary or nullable union " 6135 "containing a dictionary", 6136 [argument.location], 6137 ) 6138 6139 # Only the last argument can be variadic 6140 if variadicArgument: 6141 raise WebIDLError( 6142 "Variadic argument is not last argument", 6143 [variadicArgument.location], 6144 ) 6145 if argument.variadic: 6146 variadicArgument = argument 6147 6148 if returnType.isPromise(): 6149 overloadWithPromiseReturnType = overload 6150 else: 6151 overloadWithoutPromiseReturnType = overload 6152 6153 # Make sure either all our overloads return Promises or none do 6154 if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType: 6155 raise WebIDLError( 6156 "We have overloads with both Promise and " "non-Promise return types", 6157 [ 6158 overloadWithPromiseReturnType.location, 6159 overloadWithoutPromiseReturnType.location, 6160 ], 6161 ) 6162 6163 if overloadWithPromiseReturnType and self._legacycaller: 6164 raise WebIDLError( 6165 "May not have a Promise return type for a " "legacycaller.", 6166 [overloadWithPromiseReturnType.location], 6167 ) 6168 6169 if self.getExtendedAttribute("StaticClassOverride") and not ( 6170 self.identifier.scope.isJSImplemented() and self.isStatic() 6171 ): 6172 raise WebIDLError( 6173 "StaticClassOverride can be applied to static" 6174 " methods on JS-implemented classes only.", 6175 [self.location], 6176 ) 6177 6178 # Ensure that toJSON methods satisfy the spec constraints on them. 6179 if self.identifier.name == "toJSON": 6180 if len(self.signatures()) != 1: 6181 raise WebIDLError( 6182 "toJSON method has multiple overloads", 6183 [self._overloads[0].location, self._overloads[1].location], 6184 ) 6185 if len(self.signatures()[0][1]) != 0: 6186 raise WebIDLError("toJSON method has arguments", [self.location]) 6187 if not self.signatures()[0][0].isJSONType(): 6188 raise WebIDLError( 6189 "toJSON method has non-JSON return type", [self.location] 6190 ) 6191 6192 def overloadsForArgCount(self, argc): 6193 return [ 6194 overload 6195 for overload in self._overloads 6196 if len(overload.arguments) == argc 6197 or ( 6198 len(overload.arguments) > argc 6199 and all(arg.optional for arg in overload.arguments[argc:]) 6200 ) 6201 or ( 6202 len(overload.arguments) < argc 6203 and len(overload.arguments) > 0 6204 and overload.arguments[-1].variadic 6205 ) 6206 ] 6207 6208 def signaturesForArgCount(self, argc): 6209 return [ 6210 (overload.returnType, overload.arguments) 6211 for overload in self.overloadsForArgCount(argc) 6212 ] 6213 6214 def locationsForArgCount(self, argc): 6215 return [overload.location for overload in self.overloadsForArgCount(argc)] 6216 6217 def distinguishingIndexForArgCount(self, argc): 6218 def isValidDistinguishingIndex(idx, signatures): 6219 for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]): 6220 for (secondRetval, secondArgs) in signatures[firstSigIndex + 1 :]: 6221 if idx < len(firstArgs): 6222 firstType = firstArgs[idx].type 6223 else: 6224 assert firstArgs[-1].variadic 6225 firstType = firstArgs[-1].type 6226 if idx < len(secondArgs): 6227 secondType = secondArgs[idx].type 6228 else: 6229 assert secondArgs[-1].variadic 6230 secondType = secondArgs[-1].type 6231 if not firstType.isDistinguishableFrom(secondType): 6232 return False 6233 return True 6234 6235 signatures = self.signaturesForArgCount(argc) 6236 for idx in range(argc): 6237 if isValidDistinguishingIndex(idx, signatures): 6238 return idx 6239 # No valid distinguishing index. Time to throw 6240 locations = self.locationsForArgCount(argc) 6241 raise WebIDLError( 6242 "Signatures with %d arguments for method '%s' are not " 6243 "distinguishable" % (argc, self.identifier.name), 6244 locations, 6245 ) 6246 6247 def handleExtendedAttribute(self, attr): 6248 identifier = attr.identifier() 6249 if ( 6250 identifier == "GetterThrows" 6251 or identifier == "SetterThrows" 6252 or identifier == "GetterCanOOM" 6253 or identifier == "SetterCanOOM" 6254 or identifier == "SetterNeedsSubjectPrincipal" 6255 or identifier == "GetterNeedsSubjectPrincipal" 6256 ): 6257 raise WebIDLError( 6258 "Methods must not be flagged as " "[%s]" % identifier, 6259 [attr.location, self.location], 6260 ) 6261 elif identifier == "LegacyUnforgeable": 6262 if self.isStatic(): 6263 raise WebIDLError( 6264 "[LegacyUnforgeable] is only allowed on non-static " "methods", 6265 [attr.location, self.location], 6266 ) 6267 self._legacyUnforgeable = True 6268 elif identifier == "SameObject": 6269 raise WebIDLError( 6270 "Methods must not be flagged as [SameObject]", 6271 [attr.location, self.location], 6272 ) 6273 elif identifier == "Constant": 6274 raise WebIDLError( 6275 "Methods must not be flagged as [Constant]", 6276 [attr.location, self.location], 6277 ) 6278 elif identifier == "PutForwards": 6279 raise WebIDLError( 6280 "Only attributes support [PutForwards]", [attr.location, self.location] 6281 ) 6282 elif identifier == "LegacyLenientSetter": 6283 raise WebIDLError( 6284 "Only attributes support [LegacyLenientSetter]", 6285 [attr.location, self.location], 6286 ) 6287 elif identifier == "LenientFloat": 6288 # This is called before we've done overload resolution 6289 overloads = self._overloads 6290 assert len(overloads) == 1 6291 if not overloads[0].returnType.isVoid(): 6292 raise WebIDLError( 6293 "[LenientFloat] used on a non-void method", 6294 [attr.location, self.location], 6295 ) 6296 if not overloads[0].includesRestrictedFloatArgument(): 6297 raise WebIDLError( 6298 "[LenientFloat] used on an operation with no " 6299 "restricted float type arguments", 6300 [attr.location, self.location], 6301 ) 6302 elif identifier == "Exposed": 6303 convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) 6304 elif ( 6305 identifier == "CrossOriginCallable" 6306 or identifier == "WebGLHandlesContextLoss" 6307 ): 6308 # Known no-argument attributes. 6309 if not attr.noArguments(): 6310 raise WebIDLError( 6311 "[%s] must take no arguments" % identifier, [attr.location] 6312 ) 6313 if identifier == "CrossOriginCallable" and self.isStatic(): 6314 raise WebIDLError( 6315 "[CrossOriginCallable] is only allowed on non-static " "attributes", 6316 [attr.location, self.location], 6317 ) 6318 elif identifier == "Pure": 6319 if not attr.noArguments(): 6320 raise WebIDLError("[Pure] must take no arguments", [attr.location]) 6321 self._setDependsOn("DOMState") 6322 self._setAffects("Nothing") 6323 elif identifier == "Affects": 6324 if not attr.hasValue(): 6325 raise WebIDLError("[Affects] takes an identifier", [attr.location]) 6326 self._setAffects(attr.value()) 6327 elif identifier == "DependsOn": 6328 if not attr.hasValue(): 6329 raise WebIDLError("[DependsOn] takes an identifier", [attr.location]) 6330 self._setDependsOn(attr.value()) 6331 elif identifier == "Alias": 6332 if not attr.hasValue(): 6333 raise WebIDLError( 6334 "[Alias] takes an identifier or string", [attr.location] 6335 ) 6336 self._addAlias(attr.value()) 6337 elif identifier == "UseCounter": 6338 if self.isSpecial(): 6339 raise WebIDLError( 6340 "[UseCounter] must not be used on a special " "operation", 6341 [attr.location, self.location], 6342 ) 6343 elif identifier == "Unscopable": 6344 if not attr.noArguments(): 6345 raise WebIDLError( 6346 "[Unscopable] must take no arguments", [attr.location] 6347 ) 6348 if self.isStatic(): 6349 raise WebIDLError( 6350 "[Unscopable] is only allowed on non-static " 6351 "attributes and operations", 6352 [attr.location, self.location], 6353 ) 6354 elif identifier == "CEReactions": 6355 if not attr.noArguments(): 6356 raise WebIDLError( 6357 "[CEReactions] must take no arguments", [attr.location] 6358 ) 6359 6360 if self.isSpecial() and not self.isSetter() and not self.isDeleter(): 6361 raise WebIDLError( 6362 "[CEReactions] is only allowed on operation, " 6363 "attribute, setter, and deleter", 6364 [attr.location, self.location], 6365 ) 6366 elif identifier == "Default": 6367 if not attr.noArguments(): 6368 raise WebIDLError("[Default] must take no arguments", [attr.location]) 6369 6370 if not self.isToJSON(): 6371 raise WebIDLError( 6372 "[Default] is only allowed on toJSON operations", 6373 [attr.location, self.location], 6374 ) 6375 6376 if self.signatures()[0][0] != BuiltinTypes[IDLBuiltinType.Types.object]: 6377 raise WebIDLError( 6378 "The return type of the default toJSON " 6379 "operation must be 'object'", 6380 [attr.location, self.location], 6381 ) 6382 elif ( 6383 identifier == "Throws" 6384 or identifier == "CanOOM" 6385 or identifier == "NewObject" 6386 or identifier == "ChromeOnly" 6387 or identifier == "Pref" 6388 or identifier == "Deprecated" 6389 or identifier == "Func" 6390 or identifier == "SecureContext" 6391 or identifier == "BinaryName" 6392 or identifier == "NeedsSubjectPrincipal" 6393 or identifier == "NeedsCallerType" 6394 or identifier == "StaticClassOverride" 6395 or identifier == "NonEnumerable" 6396 or identifier == "Unexposed" 6397 or identifier == "WebExtensionStub" 6398 ): 6399 # Known attributes that we don't need to do anything with here 6400 pass 6401 else: 6402 raise WebIDLError( 6403 "Unknown extended attribute %s on method" % identifier, [attr.location] 6404 ) 6405 IDLInterfaceMember.handleExtendedAttribute(self, attr) 6406 6407 def returnsPromise(self): 6408 return self._overloads[0].returnType.isPromise() 6409 6410 def isLegacyUnforgeable(self): 6411 return self._legacyUnforgeable 6412 6413 def _getDependentObjects(self): 6414 deps = set() 6415 for overload in self._overloads: 6416 deps.update(overload._getDependentObjects()) 6417 return deps 6418 6419 6420class IDLConstructor(IDLMethod): 6421 def __init__(self, location, args, name): 6422 # We can't actually init our IDLMethod yet, because we do not know the 6423 # return type yet. Just save the info we have for now and we will init 6424 # it later. 6425 self._initLocation = location 6426 self._initArgs = args 6427 self._initName = name 6428 self._inited = False 6429 self._initExtendedAttrs = [] 6430 6431 def addExtendedAttributes(self, attrs): 6432 if self._inited: 6433 return IDLMethod.addExtendedAttributes(self, attrs) 6434 self._initExtendedAttrs.extend(attrs) 6435 6436 def handleExtendedAttribute(self, attr): 6437 identifier = attr.identifier() 6438 if ( 6439 identifier == "BinaryName" 6440 or identifier == "ChromeOnly" 6441 or identifier == "NewObject" 6442 or identifier == "SecureContext" 6443 or identifier == "Throws" 6444 or identifier == "Func" 6445 or identifier == "Pref" 6446 ): 6447 IDLMethod.handleExtendedAttribute(self, attr) 6448 elif identifier == "HTMLConstructor": 6449 if not attr.noArguments(): 6450 raise WebIDLError( 6451 "[HTMLConstructor] must take no arguments", [attr.location] 6452 ) 6453 # We shouldn't end up here for legacy factory functions. 6454 assert self.identifier.name == "constructor" 6455 6456 if any(len(sig[1]) != 0 for sig in self.signatures()): 6457 raise WebIDLError( 6458 "[HTMLConstructor] must not be applied to a " 6459 "constructor operation that has arguments.", 6460 [attr.location], 6461 ) 6462 self._htmlConstructor = True 6463 else: 6464 raise WebIDLError( 6465 "Unknown extended attribute %s on method" % identifier, [attr.location] 6466 ) 6467 6468 def reallyInit(self, parentInterface): 6469 name = self._initName 6470 location = self._initLocation 6471 identifier = IDLUnresolvedIdentifier(location, name, allowForbidden=True) 6472 retType = IDLWrapperType(parentInterface.location, parentInterface) 6473 IDLMethod.__init__( 6474 self, location, identifier, retType, self._initArgs, static=True 6475 ) 6476 self._inited = True 6477 # Propagate through whatever extended attributes we already had 6478 self.addExtendedAttributes(self._initExtendedAttrs) 6479 self._initExtendedAttrs = [] 6480 # Constructors are always NewObject. Whether they throw or not is 6481 # indicated by [Throws] annotations in the usual way. 6482 self.addExtendedAttributes( 6483 [IDLExtendedAttribute(self.location, ("NewObject",))] 6484 ) 6485 6486 6487class IDLIncludesStatement(IDLObject): 6488 def __init__(self, location, interface, mixin): 6489 IDLObject.__init__(self, location) 6490 self.interface = interface 6491 self.mixin = mixin 6492 self._finished = False 6493 6494 def finish(self, scope): 6495 if self._finished: 6496 return 6497 self._finished = True 6498 assert isinstance(self.interface, IDLIdentifierPlaceholder) 6499 assert isinstance(self.mixin, IDLIdentifierPlaceholder) 6500 interface = self.interface.finish(scope) 6501 mixin = self.mixin.finish(scope) 6502 # NOTE: we depend on not setting self.interface and 6503 # self.mixin here to keep track of the original 6504 # locations. 6505 if not isinstance(interface, IDLInterface): 6506 raise WebIDLError( 6507 "Left-hand side of 'includes' is not an " "interface", 6508 [self.interface.location, interface.location], 6509 ) 6510 if interface.isCallback(): 6511 raise WebIDLError( 6512 "Left-hand side of 'includes' is a callback " "interface", 6513 [self.interface.location, interface.location], 6514 ) 6515 if not isinstance(mixin, IDLInterfaceMixin): 6516 raise WebIDLError( 6517 "Right-hand side of 'includes' is not an " "interface mixin", 6518 [self.mixin.location, mixin.location], 6519 ) 6520 6521 mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames) 6522 6523 interface.addIncludedMixin(mixin) 6524 self.interface = interface 6525 self.mixin = mixin 6526 6527 def validate(self): 6528 pass 6529 6530 def addExtendedAttributes(self, attrs): 6531 if len(attrs) != 0: 6532 raise WebIDLError( 6533 "There are no extended attributes that are " 6534 "allowed on includes statements", 6535 [attrs[0].location, self.location], 6536 ) 6537 6538 6539class IDLExtendedAttribute(IDLObject): 6540 """ 6541 A class to represent IDL extended attributes so we can give them locations 6542 """ 6543 6544 def __init__(self, location, tuple): 6545 IDLObject.__init__(self, location) 6546 self._tuple = tuple 6547 6548 def identifier(self): 6549 return self._tuple[0] 6550 6551 def noArguments(self): 6552 return len(self._tuple) == 1 6553 6554 def hasValue(self): 6555 return len(self._tuple) >= 2 and isinstance(self._tuple[1], str) 6556 6557 def value(self): 6558 assert self.hasValue() 6559 return self._tuple[1] 6560 6561 def hasArgs(self): 6562 return ( 6563 len(self._tuple) == 2 6564 and isinstance(self._tuple[1], list) 6565 or len(self._tuple) == 3 6566 ) 6567 6568 def args(self): 6569 assert self.hasArgs() 6570 # Our args are our last element 6571 return self._tuple[-1] 6572 6573 def listValue(self): 6574 """ 6575 Backdoor for storing random data in _extendedAttrDict 6576 """ 6577 return list(self._tuple)[1:] 6578 6579 6580# Parser 6581 6582 6583class Tokenizer(object): 6584 tokens = ["INTEGER", "FLOATLITERAL", "IDENTIFIER", "STRING", "WHITESPACE", "OTHER"] 6585 6586 def t_FLOATLITERAL(self, t): 6587 r"(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN" 6588 t.value = float(t.value) 6589 return t 6590 6591 def t_INTEGER(self, t): 6592 r"-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)" 6593 try: 6594 # Can't use int(), because that doesn't handle octal properly. 6595 t.value = parseInt(t.value) 6596 except: 6597 raise WebIDLError( 6598 "Invalid integer literal", 6599 [ 6600 Location( 6601 lexer=self.lexer, 6602 lineno=self.lexer.lineno, 6603 lexpos=self.lexer.lexpos, 6604 filename=self._filename, 6605 ) 6606 ], 6607 ) 6608 return t 6609 6610 def t_IDENTIFIER(self, t): 6611 r"[_-]?[A-Za-z][0-9A-Z_a-z-]*" 6612 t.type = self.keywords.get(t.value, "IDENTIFIER") 6613 return t 6614 6615 def t_STRING(self, t): 6616 r'"[^"]*"' 6617 t.value = t.value[1:-1] 6618 return t 6619 6620 def t_WHITESPACE(self, t): 6621 r"[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+" 6622 pass 6623 6624 def t_ELLIPSIS(self, t): 6625 r"\.\.\." 6626 t.type = self.keywords.get(t.value) 6627 return t 6628 6629 def t_OTHER(self, t): 6630 r"[^\t\n\r 0-9A-Z_a-z]" 6631 t.type = self.keywords.get(t.value, "OTHER") 6632 return t 6633 6634 keywords = { 6635 "interface": "INTERFACE", 6636 "partial": "PARTIAL", 6637 "mixin": "MIXIN", 6638 "dictionary": "DICTIONARY", 6639 "exception": "EXCEPTION", 6640 "enum": "ENUM", 6641 "callback": "CALLBACK", 6642 "typedef": "TYPEDEF", 6643 "includes": "INCLUDES", 6644 "const": "CONST", 6645 "null": "NULL", 6646 "true": "TRUE", 6647 "false": "FALSE", 6648 "serializer": "SERIALIZER", 6649 "stringifier": "STRINGIFIER", 6650 "unrestricted": "UNRESTRICTED", 6651 "attribute": "ATTRIBUTE", 6652 "readonly": "READONLY", 6653 "inherit": "INHERIT", 6654 "static": "STATIC", 6655 "getter": "GETTER", 6656 "setter": "SETTER", 6657 "deleter": "DELETER", 6658 "legacycaller": "LEGACYCALLER", 6659 "optional": "OPTIONAL", 6660 "...": "ELLIPSIS", 6661 "::": "SCOPE", 6662 "DOMString": "DOMSTRING", 6663 "ByteString": "BYTESTRING", 6664 "USVString": "USVSTRING", 6665 "JSString": "JSSTRING", 6666 "UTF8String": "UTF8STRING", 6667 "any": "ANY", 6668 "boolean": "BOOLEAN", 6669 "byte": "BYTE", 6670 "double": "DOUBLE", 6671 "float": "FLOAT", 6672 "long": "LONG", 6673 "object": "OBJECT", 6674 "octet": "OCTET", 6675 "Promise": "PROMISE", 6676 "required": "REQUIRED", 6677 "sequence": "SEQUENCE", 6678 "record": "RECORD", 6679 "short": "SHORT", 6680 "unsigned": "UNSIGNED", 6681 "void": "VOID", 6682 ":": "COLON", 6683 ";": "SEMICOLON", 6684 "{": "LBRACE", 6685 "}": "RBRACE", 6686 "(": "LPAREN", 6687 ")": "RPAREN", 6688 "[": "LBRACKET", 6689 "]": "RBRACKET", 6690 "?": "QUESTIONMARK", 6691 ",": "COMMA", 6692 "=": "EQUALS", 6693 "<": "LT", 6694 ">": "GT", 6695 "ArrayBuffer": "ARRAYBUFFER", 6696 "or": "OR", 6697 "maplike": "MAPLIKE", 6698 "setlike": "SETLIKE", 6699 "iterable": "ITERABLE", 6700 "namespace": "NAMESPACE", 6701 "ReadableStream": "READABLESTREAM", 6702 "constructor": "CONSTRUCTOR", 6703 "symbol": "SYMBOL", 6704 "async": "ASYNC", 6705 } 6706 6707 tokens.extend(keywords.values()) 6708 6709 def t_error(self, t): 6710 raise WebIDLError( 6711 "Unrecognized Input", 6712 [ 6713 Location( 6714 lexer=self.lexer, 6715 lineno=self.lexer.lineno, 6716 lexpos=self.lexer.lexpos, 6717 filename=self.filename, 6718 ) 6719 ], 6720 ) 6721 6722 def __init__(self, outputdir, lexer=None): 6723 if lexer: 6724 self.lexer = lexer 6725 else: 6726 self.lexer = lex.lex(object=self, reflags=re.DOTALL) 6727 6728 6729class SqueakyCleanLogger(object): 6730 errorWhitelist = [ 6731 # Web IDL defines the WHITESPACE token, but doesn't actually 6732 # use it ... so far. 6733 "Token 'WHITESPACE' defined, but not used", 6734 # And that means we have an unused token 6735 "There is 1 unused token", 6736 # Web IDL defines a OtherOrComma rule that's only used in 6737 # ExtendedAttributeInner, which we don't use yet. 6738 "Rule 'OtherOrComma' defined, but not used", 6739 # And an unused rule 6740 "There is 1 unused rule", 6741 # And the OtherOrComma grammar symbol is unreachable. 6742 "Symbol 'OtherOrComma' is unreachable", 6743 # Which means the Other symbol is unreachable. 6744 "Symbol 'Other' is unreachable", 6745 ] 6746 6747 def __init__(self): 6748 self.errors = [] 6749 6750 def debug(self, msg, *args, **kwargs): 6751 pass 6752 6753 info = debug 6754 6755 def warning(self, msg, *args, **kwargs): 6756 if ( 6757 msg == "%s:%d: Rule %r defined, but not used" 6758 or msg == "%s:%d: Rule '%s' defined, but not used" 6759 ): 6760 # Munge things so we don't have to hardcode filenames and 6761 # line numbers in our whitelist. 6762 whitelistmsg = "Rule %r defined, but not used" 6763 whitelistargs = args[2:] 6764 else: 6765 whitelistmsg = msg 6766 whitelistargs = args 6767 if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist: 6768 self.errors.append(msg % args) 6769 6770 error = warning 6771 6772 def reportGrammarErrors(self): 6773 if self.errors: 6774 raise WebIDLError("\n".join(self.errors), []) 6775 6776 6777class Parser(Tokenizer): 6778 def getLocation(self, p, i): 6779 return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename) 6780 6781 def globalScope(self): 6782 return self._globalScope 6783 6784 # The p_Foo functions here must match the WebIDL spec's grammar. 6785 # It's acceptable to split things at '|' boundaries. 6786 def p_Definitions(self, p): 6787 """ 6788 Definitions : ExtendedAttributeList Definition Definitions 6789 """ 6790 if p[2]: 6791 p[0] = [p[2]] 6792 p[2].addExtendedAttributes(p[1]) 6793 else: 6794 assert not p[1] 6795 p[0] = [] 6796 6797 p[0].extend(p[3]) 6798 6799 def p_DefinitionsEmpty(self, p): 6800 """ 6801 Definitions : 6802 """ 6803 p[0] = [] 6804 6805 def p_Definition(self, p): 6806 """ 6807 Definition : CallbackOrInterfaceOrMixin 6808 | Namespace 6809 | Partial 6810 | Dictionary 6811 | Exception 6812 | Enum 6813 | Typedef 6814 | IncludesStatement 6815 """ 6816 p[0] = p[1] 6817 assert p[1] # We might not have implemented something ... 6818 6819 def p_CallbackOrInterfaceOrMixinCallback(self, p): 6820 """ 6821 CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface 6822 """ 6823 if p[2].isInterface(): 6824 assert isinstance(p[2], IDLInterface) 6825 p[2].setCallback(True) 6826 6827 p[0] = p[2] 6828 6829 def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p): 6830 """ 6831 CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin 6832 """ 6833 p[0] = p[2] 6834 6835 def p_CallbackRestOrInterface(self, p): 6836 """ 6837 CallbackRestOrInterface : CallbackRest 6838 | CallbackConstructorRest 6839 | CallbackInterface 6840 """ 6841 assert p[1] 6842 p[0] = p[1] 6843 6844 def handleNonPartialObject( 6845 self, location, identifier, constructor, constructorArgs, nonPartialArgs 6846 ): 6847 """ 6848 This handles non-partial objects (interfaces, namespaces and 6849 dictionaries) by checking for an existing partial object, and promoting 6850 it to non-partial as needed. The return value is the non-partial 6851 object. 6852 6853 constructorArgs are all the args for the constructor except the last 6854 one: isKnownNonPartial. 6855 6856 nonPartialArgs are the args for the setNonPartial call. 6857 """ 6858 # The name of the class starts with "IDL", so strip that off. 6859 # Also, starts with a capital letter after that, so nix that 6860 # as well. 6861 prettyname = constructor.__name__[3:].lower() 6862 6863 try: 6864 existingObj = self.globalScope()._lookupIdentifier(identifier) 6865 if existingObj: 6866 if not isinstance(existingObj, constructor): 6867 raise WebIDLError( 6868 "%s has the same name as " 6869 "non-%s object" % (prettyname.capitalize(), prettyname), 6870 [location, existingObj.location], 6871 ) 6872 existingObj.setNonPartial(*nonPartialArgs) 6873 return existingObj 6874 except Exception as ex: 6875 if isinstance(ex, WebIDLError): 6876 raise ex 6877 pass 6878 6879 # True for isKnownNonPartial 6880 return constructor(*(constructorArgs + [True])) 6881 6882 def p_InterfaceOrMixin(self, p): 6883 """ 6884 InterfaceOrMixin : InterfaceRest 6885 | MixinRest 6886 """ 6887 p[0] = p[1] 6888 6889 def p_CallbackInterface(self, p): 6890 """ 6891 CallbackInterface : INTERFACE InterfaceRest 6892 """ 6893 p[0] = p[2] 6894 6895 def p_InterfaceRest(self, p): 6896 """ 6897 InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON 6898 """ 6899 location = self.getLocation(p, 1) 6900 identifier = IDLUnresolvedIdentifier(location, p[1]) 6901 members = p[4] 6902 parent = p[2] 6903 6904 p[0] = self.handleNonPartialObject( 6905 location, 6906 identifier, 6907 IDLInterface, 6908 [location, self.globalScope(), identifier, parent, members], 6909 [location, parent, members], 6910 ) 6911 6912 def p_InterfaceForwardDecl(self, p): 6913 """ 6914 InterfaceRest : IDENTIFIER SEMICOLON 6915 """ 6916 location = self.getLocation(p, 1) 6917 identifier = IDLUnresolvedIdentifier(location, p[1]) 6918 6919 try: 6920 if self.globalScope()._lookupIdentifier(identifier): 6921 p[0] = self.globalScope()._lookupIdentifier(identifier) 6922 if not isinstance(p[0], IDLExternalInterface): 6923 raise WebIDLError( 6924 "Name collision between external " 6925 "interface declaration for identifier " 6926 "%s and %s" % (identifier.name, p[0]), 6927 [location, p[0].location], 6928 ) 6929 return 6930 except Exception as ex: 6931 if isinstance(ex, WebIDLError): 6932 raise ex 6933 pass 6934 6935 p[0] = IDLExternalInterface(location, self.globalScope(), identifier) 6936 6937 def p_MixinRest(self, p): 6938 """ 6939 MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON 6940 """ 6941 location = self.getLocation(p, 1) 6942 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 6943 members = p[4] 6944 6945 p[0] = self.handleNonPartialObject( 6946 location, 6947 identifier, 6948 IDLInterfaceMixin, 6949 [location, self.globalScope(), identifier, members], 6950 [location, members], 6951 ) 6952 6953 def p_Namespace(self, p): 6954 """ 6955 Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON 6956 """ 6957 location = self.getLocation(p, 1) 6958 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 6959 members = p[4] 6960 6961 p[0] = self.handleNonPartialObject( 6962 location, 6963 identifier, 6964 IDLNamespace, 6965 [location, self.globalScope(), identifier, members], 6966 [location, None, members], 6967 ) 6968 6969 def p_Partial(self, p): 6970 """ 6971 Partial : PARTIAL PartialDefinition 6972 """ 6973 p[0] = p[2] 6974 6975 def p_PartialDefinitionInterface(self, p): 6976 """ 6977 PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin 6978 """ 6979 p[0] = p[2] 6980 6981 def p_PartialDefinition(self, p): 6982 """ 6983 PartialDefinition : PartialNamespace 6984 | PartialDictionary 6985 """ 6986 p[0] = p[1] 6987 6988 def handlePartialObject( 6989 self, 6990 location, 6991 identifier, 6992 nonPartialConstructor, 6993 nonPartialConstructorArgs, 6994 partialConstructorArgs, 6995 ): 6996 """ 6997 This handles partial objects (interfaces, namespaces and dictionaries) 6998 by checking for an existing non-partial object, and adding ourselves to 6999 it as needed. The return value is our partial object. We use 7000 IDLPartialInterfaceOrNamespace for partial interfaces or namespaces, 7001 and IDLPartialDictionary for partial dictionaries. 7002 7003 nonPartialConstructorArgs are all the args for the non-partial 7004 constructor except the last two: members and isKnownNonPartial. 7005 7006 partialConstructorArgs are the arguments for the partial object 7007 constructor, except the last one (the non-partial object). 7008 """ 7009 # The name of the class starts with "IDL", so strip that off. 7010 # Also, starts with a capital letter after that, so nix that 7011 # as well. 7012 prettyname = nonPartialConstructor.__name__[3:].lower() 7013 7014 nonPartialObject = None 7015 try: 7016 nonPartialObject = self.globalScope()._lookupIdentifier(identifier) 7017 if nonPartialObject: 7018 if not isinstance(nonPartialObject, nonPartialConstructor): 7019 raise WebIDLError( 7020 "Partial %s has the same name as " 7021 "non-%s object" % (prettyname, prettyname), 7022 [location, nonPartialObject.location], 7023 ) 7024 except Exception as ex: 7025 if isinstance(ex, WebIDLError): 7026 raise ex 7027 pass 7028 7029 if not nonPartialObject: 7030 nonPartialObject = nonPartialConstructor( 7031 # No members, False for isKnownNonPartial 7032 *(nonPartialConstructorArgs), 7033 members=[], 7034 isKnownNonPartial=False 7035 ) 7036 7037 partialObject = None 7038 if isinstance(nonPartialObject, IDLDictionary): 7039 partialObject = IDLPartialDictionary( 7040 *(partialConstructorArgs + [nonPartialObject]) 7041 ) 7042 elif isinstance( 7043 nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace) 7044 ): 7045 partialObject = IDLPartialInterfaceOrNamespace( 7046 *(partialConstructorArgs + [nonPartialObject]) 7047 ) 7048 else: 7049 raise WebIDLError( 7050 "Unknown partial object type %s" % type(partialObject), [location] 7051 ) 7052 7053 return partialObject 7054 7055 def p_PartialInterfaceOrPartialMixin(self, p): 7056 """ 7057 PartialInterfaceOrPartialMixin : PartialInterfaceRest 7058 | PartialMixinRest 7059 """ 7060 p[0] = p[1] 7061 7062 def p_PartialInterfaceRest(self, p): 7063 """ 7064 PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON 7065 """ 7066 location = self.getLocation(p, 1) 7067 identifier = IDLUnresolvedIdentifier(location, p[1]) 7068 members = p[3] 7069 7070 p[0] = self.handlePartialObject( 7071 location, 7072 identifier, 7073 IDLInterface, 7074 [location, self.globalScope(), identifier, None], 7075 [location, identifier, members], 7076 ) 7077 7078 def p_PartialMixinRest(self, p): 7079 """ 7080 PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON 7081 """ 7082 location = self.getLocation(p, 1) 7083 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7084 members = p[4] 7085 7086 p[0] = self.handlePartialObject( 7087 location, 7088 identifier, 7089 IDLInterfaceMixin, 7090 [location, self.globalScope(), identifier], 7091 [location, identifier, members], 7092 ) 7093 7094 def p_PartialNamespace(self, p): 7095 """ 7096 PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON 7097 """ 7098 location = self.getLocation(p, 1) 7099 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7100 members = p[4] 7101 7102 p[0] = self.handlePartialObject( 7103 location, 7104 identifier, 7105 IDLNamespace, 7106 [location, self.globalScope(), identifier], 7107 [location, identifier, members], 7108 ) 7109 7110 def p_PartialDictionary(self, p): 7111 """ 7112 PartialDictionary : DICTIONARY IDENTIFIER LBRACE DictionaryMembers RBRACE SEMICOLON 7113 """ 7114 location = self.getLocation(p, 1) 7115 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7116 members = p[4] 7117 7118 p[0] = self.handlePartialObject( 7119 location, 7120 identifier, 7121 IDLDictionary, 7122 [location, self.globalScope(), identifier], 7123 [location, identifier, members], 7124 ) 7125 7126 def p_Inheritance(self, p): 7127 """ 7128 Inheritance : COLON ScopedName 7129 """ 7130 p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2]) 7131 7132 def p_InheritanceEmpty(self, p): 7133 """ 7134 Inheritance : 7135 """ 7136 pass 7137 7138 def p_InterfaceMembers(self, p): 7139 """ 7140 InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers 7141 """ 7142 p[0] = [p[2]] 7143 7144 assert not p[1] or p[2] 7145 p[2].addExtendedAttributes(p[1]) 7146 7147 p[0].extend(p[3]) 7148 7149 def p_InterfaceMembersEmpty(self, p): 7150 """ 7151 InterfaceMembers : 7152 """ 7153 p[0] = [] 7154 7155 def p_InterfaceMember(self, p): 7156 """ 7157 InterfaceMember : PartialInterfaceMember 7158 | Constructor 7159 """ 7160 p[0] = p[1] 7161 7162 def p_Constructor(self, p): 7163 """ 7164 Constructor : CONSTRUCTOR LPAREN ArgumentList RPAREN SEMICOLON 7165 """ 7166 p[0] = IDLConstructor(self.getLocation(p, 1), p[3], "constructor") 7167 7168 def p_PartialInterfaceMembers(self, p): 7169 """ 7170 PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers 7171 """ 7172 p[0] = [p[2]] 7173 7174 assert not p[1] or p[2] 7175 p[2].addExtendedAttributes(p[1]) 7176 7177 p[0].extend(p[3]) 7178 7179 def p_PartialInterfaceMembersEmpty(self, p): 7180 """ 7181 PartialInterfaceMembers : 7182 """ 7183 p[0] = [] 7184 7185 def p_PartialInterfaceMember(self, p): 7186 """ 7187 PartialInterfaceMember : Const 7188 | AttributeOrOperationOrMaplikeOrSetlikeOrIterable 7189 """ 7190 p[0] = p[1] 7191 7192 def p_MixinMembersEmpty(self, p): 7193 """ 7194 MixinMembers : 7195 """ 7196 p[0] = [] 7197 7198 def p_MixinMembers(self, p): 7199 """ 7200 MixinMembers : ExtendedAttributeList MixinMember MixinMembers 7201 """ 7202 p[0] = [p[2]] 7203 7204 assert not p[1] or p[2] 7205 p[2].addExtendedAttributes(p[1]) 7206 7207 p[0].extend(p[3]) 7208 7209 def p_MixinMember(self, p): 7210 """ 7211 MixinMember : Const 7212 | Attribute 7213 | Operation 7214 """ 7215 p[0] = p[1] 7216 7217 def p_Dictionary(self, p): 7218 """ 7219 Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON 7220 """ 7221 location = self.getLocation(p, 1) 7222 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7223 members = p[5] 7224 p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members) 7225 7226 def p_DictionaryMembers(self, p): 7227 """ 7228 DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers 7229 | 7230 """ 7231 if len(p) == 1: 7232 # We're at the end of the list 7233 p[0] = [] 7234 return 7235 p[2].addExtendedAttributes(p[1]) 7236 p[0] = [p[2]] 7237 p[0].extend(p[3]) 7238 7239 def p_DictionaryMemberRequired(self, p): 7240 """ 7241 DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON 7242 """ 7243 # These quack a lot like required arguments, so just treat them that way. 7244 t = p[2] 7245 assert isinstance(t, IDLType) 7246 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) 7247 7248 p[0] = IDLArgument( 7249 self.getLocation(p, 3), 7250 identifier, 7251 t, 7252 optional=False, 7253 defaultValue=None, 7254 variadic=False, 7255 dictionaryMember=True, 7256 ) 7257 7258 def p_DictionaryMember(self, p): 7259 """ 7260 DictionaryMember : Type IDENTIFIER Default SEMICOLON 7261 """ 7262 # These quack a lot like optional arguments, so just treat them that way. 7263 t = p[1] 7264 assert isinstance(t, IDLType) 7265 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7266 defaultValue = p[3] 7267 7268 # Any attributes that precede this may apply to the type, so 7269 # we configure the argument to forward type attributes down instead of producing 7270 # a parse error 7271 p[0] = IDLArgument( 7272 self.getLocation(p, 2), 7273 identifier, 7274 t, 7275 optional=True, 7276 defaultValue=defaultValue, 7277 variadic=False, 7278 dictionaryMember=True, 7279 allowTypeAttributes=True, 7280 ) 7281 7282 def p_Default(self, p): 7283 """ 7284 Default : EQUALS DefaultValue 7285 | 7286 """ 7287 if len(p) > 1: 7288 p[0] = p[2] 7289 else: 7290 p[0] = None 7291 7292 def p_DefaultValue(self, p): 7293 """ 7294 DefaultValue : ConstValue 7295 | LBRACKET RBRACKET 7296 | LBRACE RBRACE 7297 """ 7298 if len(p) == 2: 7299 p[0] = p[1] 7300 else: 7301 assert len(p) == 3 # Must be [] or {} 7302 if p[1] == "[": 7303 p[0] = IDLEmptySequenceValue(self.getLocation(p, 1)) 7304 else: 7305 assert p[1] == "{" 7306 p[0] = IDLDefaultDictionaryValue(self.getLocation(p, 1)) 7307 7308 def p_DefaultValueNull(self, p): 7309 """ 7310 DefaultValue : NULL 7311 """ 7312 p[0] = IDLNullValue(self.getLocation(p, 1)) 7313 7314 def p_Exception(self, p): 7315 """ 7316 Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON 7317 """ 7318 pass 7319 7320 def p_Enum(self, p): 7321 """ 7322 Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON 7323 """ 7324 location = self.getLocation(p, 1) 7325 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7326 7327 values = p[4] 7328 assert values 7329 p[0] = IDLEnum(location, self.globalScope(), identifier, values) 7330 7331 def p_EnumValueList(self, p): 7332 """ 7333 EnumValueList : STRING EnumValueListComma 7334 """ 7335 p[0] = [p[1]] 7336 p[0].extend(p[2]) 7337 7338 def p_EnumValueListComma(self, p): 7339 """ 7340 EnumValueListComma : COMMA EnumValueListString 7341 """ 7342 p[0] = p[2] 7343 7344 def p_EnumValueListCommaEmpty(self, p): 7345 """ 7346 EnumValueListComma : 7347 """ 7348 p[0] = [] 7349 7350 def p_EnumValueListString(self, p): 7351 """ 7352 EnumValueListString : STRING EnumValueListComma 7353 """ 7354 p[0] = [p[1]] 7355 p[0].extend(p[2]) 7356 7357 def p_EnumValueListStringEmpty(self, p): 7358 """ 7359 EnumValueListString : 7360 """ 7361 p[0] = [] 7362 7363 def p_CallbackRest(self, p): 7364 """ 7365 CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON 7366 """ 7367 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) 7368 p[0] = IDLCallback( 7369 self.getLocation(p, 1), 7370 self.globalScope(), 7371 identifier, 7372 p[3], 7373 p[5], 7374 isConstructor=False, 7375 ) 7376 7377 def p_CallbackConstructorRest(self, p): 7378 """ 7379 CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON 7380 """ 7381 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) 7382 p[0] = IDLCallback( 7383 self.getLocation(p, 2), 7384 self.globalScope(), 7385 identifier, 7386 p[4], 7387 p[6], 7388 isConstructor=True, 7389 ) 7390 7391 def p_ExceptionMembers(self, p): 7392 """ 7393 ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers 7394 | 7395 """ 7396 pass 7397 7398 def p_Typedef(self, p): 7399 """ 7400 Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON 7401 """ 7402 typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), p[2], p[3]) 7403 p[0] = typedef 7404 7405 def p_IncludesStatement(self, p): 7406 """ 7407 IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON 7408 """ 7409 assert p[2] == "includes" 7410 interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1]) 7411 mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3]) 7412 p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin) 7413 7414 def p_Const(self, p): 7415 """ 7416 Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON 7417 """ 7418 location = self.getLocation(p, 1) 7419 type = p[2] 7420 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) 7421 value = p[5] 7422 p[0] = IDLConst(location, identifier, type, value) 7423 7424 def p_ConstValueBoolean(self, p): 7425 """ 7426 ConstValue : BooleanLiteral 7427 """ 7428 location = self.getLocation(p, 1) 7429 booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean] 7430 p[0] = IDLValue(location, booleanType, p[1]) 7431 7432 def p_ConstValueInteger(self, p): 7433 """ 7434 ConstValue : INTEGER 7435 """ 7436 location = self.getLocation(p, 1) 7437 7438 # We don't know ahead of time what type the integer literal is. 7439 # Determine the smallest type it could possibly fit in and use that. 7440 integerType = matchIntegerValueToType(p[1]) 7441 if integerType is None: 7442 raise WebIDLError("Integer literal out of range", [location]) 7443 7444 p[0] = IDLValue(location, integerType, p[1]) 7445 7446 def p_ConstValueFloat(self, p): 7447 """ 7448 ConstValue : FLOATLITERAL 7449 """ 7450 location = self.getLocation(p, 1) 7451 p[0] = IDLValue( 7452 location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1] 7453 ) 7454 7455 def p_ConstValueString(self, p): 7456 """ 7457 ConstValue : STRING 7458 """ 7459 location = self.getLocation(p, 1) 7460 stringType = BuiltinTypes[IDLBuiltinType.Types.domstring] 7461 p[0] = IDLValue(location, stringType, p[1]) 7462 7463 def p_BooleanLiteralTrue(self, p): 7464 """ 7465 BooleanLiteral : TRUE 7466 """ 7467 p[0] = True 7468 7469 def p_BooleanLiteralFalse(self, p): 7470 """ 7471 BooleanLiteral : FALSE 7472 """ 7473 p[0] = False 7474 7475 def p_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p): 7476 """ 7477 AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute 7478 | Maplike 7479 | Setlike 7480 | Iterable 7481 | Operation 7482 """ 7483 p[0] = p[1] 7484 7485 def p_Iterable(self, p): 7486 """ 7487 Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON 7488 | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON 7489 """ 7490 location = self.getLocation(p, 2) 7491 identifier = IDLUnresolvedIdentifier( 7492 location, "__iterable", allowDoubleUnderscore=True 7493 ) 7494 if len(p) > 6: 7495 keyType = p[3] 7496 valueType = p[5] 7497 else: 7498 keyType = None 7499 valueType = p[3] 7500 7501 p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope()) 7502 7503 def p_Setlike(self, p): 7504 """ 7505 Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON 7506 """ 7507 readonly = p[1] 7508 maplikeOrSetlikeType = p[2] 7509 location = self.getLocation(p, 2) 7510 identifier = IDLUnresolvedIdentifier( 7511 location, "__setlike", allowDoubleUnderscore=True 7512 ) 7513 keyType = p[4] 7514 valueType = keyType 7515 p[0] = IDLMaplikeOrSetlike( 7516 location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType 7517 ) 7518 7519 def p_Maplike(self, p): 7520 """ 7521 Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON 7522 """ 7523 readonly = p[1] 7524 maplikeOrSetlikeType = p[2] 7525 location = self.getLocation(p, 2) 7526 identifier = IDLUnresolvedIdentifier( 7527 location, "__maplike", allowDoubleUnderscore=True 7528 ) 7529 keyType = p[4] 7530 valueType = p[6] 7531 p[0] = IDLMaplikeOrSetlike( 7532 location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType 7533 ) 7534 7535 def p_AttributeWithQualifier(self, p): 7536 """ 7537 Attribute : Qualifier AttributeRest 7538 """ 7539 static = IDLInterfaceMember.Special.Static in p[1] 7540 stringifier = IDLInterfaceMember.Special.Stringifier in p[1] 7541 (location, identifier, type, readonly) = p[2] 7542 p[0] = IDLAttribute( 7543 location, identifier, type, readonly, static=static, stringifier=stringifier 7544 ) 7545 7546 def p_AttributeInherited(self, p): 7547 """ 7548 Attribute : INHERIT AttributeRest 7549 """ 7550 (location, identifier, type, readonly) = p[2] 7551 p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True) 7552 7553 def p_Attribute(self, p): 7554 """ 7555 Attribute : AttributeRest 7556 """ 7557 (location, identifier, type, readonly) = p[1] 7558 p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False) 7559 7560 def p_AttributeRest(self, p): 7561 """ 7562 AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON 7563 """ 7564 location = self.getLocation(p, 2) 7565 readonly = p[1] 7566 t = p[3] 7567 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4]) 7568 p[0] = (location, identifier, t, readonly) 7569 7570 def p_ReadOnly(self, p): 7571 """ 7572 ReadOnly : READONLY 7573 """ 7574 p[0] = True 7575 7576 def p_ReadOnlyEmpty(self, p): 7577 """ 7578 ReadOnly : 7579 """ 7580 p[0] = False 7581 7582 def p_Operation(self, p): 7583 """ 7584 Operation : Qualifiers OperationRest 7585 """ 7586 qualifiers = p[1] 7587 7588 # Disallow duplicates in the qualifier set 7589 if not len(set(qualifiers)) == len(qualifiers): 7590 raise WebIDLError( 7591 "Duplicate qualifiers are not allowed", [self.getLocation(p, 1)] 7592 ) 7593 7594 static = IDLInterfaceMember.Special.Static in p[1] 7595 # If static is there that's all that's allowed. This is disallowed 7596 # by the parser, so we can assert here. 7597 assert not static or len(qualifiers) == 1 7598 7599 stringifier = IDLInterfaceMember.Special.Stringifier in p[1] 7600 # If stringifier is there that's all that's allowed. This is disallowed 7601 # by the parser, so we can assert here. 7602 assert not stringifier or len(qualifiers) == 1 7603 7604 getter = True if IDLMethod.Special.Getter in p[1] else False 7605 setter = True if IDLMethod.Special.Setter in p[1] else False 7606 deleter = True if IDLMethod.Special.Deleter in p[1] else False 7607 legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False 7608 7609 if getter or deleter: 7610 if setter: 7611 raise WebIDLError( 7612 "getter and deleter are incompatible with setter", 7613 [self.getLocation(p, 1)], 7614 ) 7615 7616 (returnType, identifier, arguments) = p[2] 7617 7618 assert isinstance(returnType, IDLType) 7619 7620 specialType = IDLMethod.NamedOrIndexed.Neither 7621 7622 if getter or deleter: 7623 if len(arguments) != 1: 7624 raise WebIDLError( 7625 "%s has wrong number of arguments" 7626 % ("getter" if getter else "deleter"), 7627 [self.getLocation(p, 2)], 7628 ) 7629 argType = arguments[0].type 7630 if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: 7631 specialType = IDLMethod.NamedOrIndexed.Named 7632 elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: 7633 specialType = IDLMethod.NamedOrIndexed.Indexed 7634 if deleter: 7635 raise WebIDLError( 7636 "There is no such thing as an indexed deleter.", 7637 [self.getLocation(p, 1)], 7638 ) 7639 else: 7640 raise WebIDLError( 7641 "%s has wrong argument type (must be DOMString or UnsignedLong)" 7642 % ("getter" if getter else "deleter"), 7643 [arguments[0].location], 7644 ) 7645 if arguments[0].optional or arguments[0].variadic: 7646 raise WebIDLError( 7647 "%s cannot have %s argument" 7648 % ( 7649 "getter" if getter else "deleter", 7650 "optional" if arguments[0].optional else "variadic", 7651 ), 7652 [arguments[0].location], 7653 ) 7654 if getter: 7655 if returnType.isVoid(): 7656 raise WebIDLError( 7657 "getter cannot have void return type", [self.getLocation(p, 2)] 7658 ) 7659 if setter: 7660 if len(arguments) != 2: 7661 raise WebIDLError( 7662 "setter has wrong number of arguments", [self.getLocation(p, 2)] 7663 ) 7664 argType = arguments[0].type 7665 if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: 7666 specialType = IDLMethod.NamedOrIndexed.Named 7667 elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: 7668 specialType = IDLMethod.NamedOrIndexed.Indexed 7669 else: 7670 raise WebIDLError( 7671 "settter has wrong argument type (must be DOMString or UnsignedLong)", 7672 [arguments[0].location], 7673 ) 7674 if arguments[0].optional or arguments[0].variadic: 7675 raise WebIDLError( 7676 "setter cannot have %s argument" 7677 % ("optional" if arguments[0].optional else "variadic"), 7678 [arguments[0].location], 7679 ) 7680 if arguments[1].optional or arguments[1].variadic: 7681 raise WebIDLError( 7682 "setter cannot have %s argument" 7683 % ("optional" if arguments[1].optional else "variadic"), 7684 [arguments[1].location], 7685 ) 7686 7687 if stringifier: 7688 if len(arguments) != 0: 7689 raise WebIDLError( 7690 "stringifier has wrong number of arguments", 7691 [self.getLocation(p, 2)], 7692 ) 7693 if not returnType.isDOMString(): 7694 raise WebIDLError( 7695 "stringifier must have DOMString return type", 7696 [self.getLocation(p, 2)], 7697 ) 7698 7699 # identifier might be None. This is only permitted for special methods. 7700 if not identifier: 7701 if ( 7702 not getter 7703 and not setter 7704 and not deleter 7705 and not legacycaller 7706 and not stringifier 7707 ): 7708 raise WebIDLError( 7709 "Identifier required for non-special methods", 7710 [self.getLocation(p, 2)], 7711 ) 7712 7713 location = BuiltinLocation("<auto-generated-identifier>") 7714 identifier = IDLUnresolvedIdentifier( 7715 location, 7716 "__%s%s%s%s%s%s" 7717 % ( 7718 "named" 7719 if specialType == IDLMethod.NamedOrIndexed.Named 7720 else "indexed" 7721 if specialType == IDLMethod.NamedOrIndexed.Indexed 7722 else "", 7723 "getter" if getter else "", 7724 "setter" if setter else "", 7725 "deleter" if deleter else "", 7726 "legacycaller" if legacycaller else "", 7727 "stringifier" if stringifier else "", 7728 ), 7729 allowDoubleUnderscore=True, 7730 ) 7731 7732 method = IDLMethod( 7733 self.getLocation(p, 2), 7734 identifier, 7735 returnType, 7736 arguments, 7737 static=static, 7738 getter=getter, 7739 setter=setter, 7740 deleter=deleter, 7741 specialType=specialType, 7742 legacycaller=legacycaller, 7743 stringifier=stringifier, 7744 ) 7745 p[0] = method 7746 7747 def p_Stringifier(self, p): 7748 """ 7749 Operation : STRINGIFIER SEMICOLON 7750 """ 7751 identifier = IDLUnresolvedIdentifier( 7752 BuiltinLocation("<auto-generated-identifier>"), 7753 "__stringifier", 7754 allowDoubleUnderscore=True, 7755 ) 7756 method = IDLMethod( 7757 self.getLocation(p, 1), 7758 identifier, 7759 returnType=BuiltinTypes[IDLBuiltinType.Types.domstring], 7760 arguments=[], 7761 stringifier=True, 7762 ) 7763 p[0] = method 7764 7765 def p_QualifierStatic(self, p): 7766 """ 7767 Qualifier : STATIC 7768 """ 7769 p[0] = [IDLInterfaceMember.Special.Static] 7770 7771 def p_QualifierStringifier(self, p): 7772 """ 7773 Qualifier : STRINGIFIER 7774 """ 7775 p[0] = [IDLInterfaceMember.Special.Stringifier] 7776 7777 def p_Qualifiers(self, p): 7778 """ 7779 Qualifiers : Qualifier 7780 | Specials 7781 """ 7782 p[0] = p[1] 7783 7784 def p_Specials(self, p): 7785 """ 7786 Specials : Special Specials 7787 """ 7788 p[0] = [p[1]] 7789 p[0].extend(p[2]) 7790 7791 def p_SpecialsEmpty(self, p): 7792 """ 7793 Specials : 7794 """ 7795 p[0] = [] 7796 7797 def p_SpecialGetter(self, p): 7798 """ 7799 Special : GETTER 7800 """ 7801 p[0] = IDLMethod.Special.Getter 7802 7803 def p_SpecialSetter(self, p): 7804 """ 7805 Special : SETTER 7806 """ 7807 p[0] = IDLMethod.Special.Setter 7808 7809 def p_SpecialDeleter(self, p): 7810 """ 7811 Special : DELETER 7812 """ 7813 p[0] = IDLMethod.Special.Deleter 7814 7815 def p_SpecialLegacyCaller(self, p): 7816 """ 7817 Special : LEGACYCALLER 7818 """ 7819 p[0] = IDLMethod.Special.LegacyCaller 7820 7821 def p_OperationRest(self, p): 7822 """ 7823 OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON 7824 """ 7825 p[0] = (p[1], p[2], p[4]) 7826 7827 def p_OptionalIdentifier(self, p): 7828 """ 7829 OptionalIdentifier : IDENTIFIER 7830 """ 7831 p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) 7832 7833 def p_OptionalIdentifierEmpty(self, p): 7834 """ 7835 OptionalIdentifier : 7836 """ 7837 pass 7838 7839 def p_ArgumentList(self, p): 7840 """ 7841 ArgumentList : Argument Arguments 7842 """ 7843 p[0] = [p[1]] if p[1] else [] 7844 p[0].extend(p[2]) 7845 7846 def p_ArgumentListEmpty(self, p): 7847 """ 7848 ArgumentList : 7849 """ 7850 p[0] = [] 7851 7852 def p_Arguments(self, p): 7853 """ 7854 Arguments : COMMA Argument Arguments 7855 """ 7856 p[0] = [p[2]] if p[2] else [] 7857 p[0].extend(p[3]) 7858 7859 def p_ArgumentsEmpty(self, p): 7860 """ 7861 Arguments : 7862 """ 7863 p[0] = [] 7864 7865 def p_Argument(self, p): 7866 """ 7867 Argument : ExtendedAttributeList ArgumentRest 7868 """ 7869 p[0] = p[2] 7870 p[0].addExtendedAttributes(p[1]) 7871 7872 def p_ArgumentRestOptional(self, p): 7873 """ 7874 ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default 7875 """ 7876 t = p[2] 7877 assert isinstance(t, IDLType) 7878 # Arg names can be reserved identifiers 7879 identifier = IDLUnresolvedIdentifier( 7880 self.getLocation(p, 3), p[3], allowForbidden=True 7881 ) 7882 7883 defaultValue = p[4] 7884 7885 # We can't test t.isAny() here and give it a default value as needed, 7886 # since at this point t is not a fully resolved type yet (e.g. it might 7887 # be a typedef). We'll handle the 'any' case in IDLArgument.complete. 7888 7889 p[0] = IDLArgument( 7890 self.getLocation(p, 3), identifier, t, True, defaultValue, False 7891 ) 7892 7893 def p_ArgumentRest(self, p): 7894 """ 7895 ArgumentRest : Type Ellipsis ArgumentName 7896 """ 7897 t = p[1] 7898 assert isinstance(t, IDLType) 7899 # Arg names can be reserved identifiers 7900 identifier = IDLUnresolvedIdentifier( 7901 self.getLocation(p, 3), p[3], allowForbidden=True 7902 ) 7903 7904 variadic = p[2] 7905 7906 # We can't test t.isAny() here and give it a default value as needed, 7907 # since at this point t is not a fully resolved type yet (e.g. it might 7908 # be a typedef). We'll handle the 'any' case in IDLArgument.complete. 7909 7910 # variadic implies optional 7911 # Any attributes that precede this may apply to the type, so 7912 # we configure the argument to forward type attributes down instead of producing 7913 # a parse error 7914 p[0] = IDLArgument( 7915 self.getLocation(p, 3), 7916 identifier, 7917 t, 7918 variadic, 7919 None, 7920 variadic, 7921 allowTypeAttributes=True, 7922 ) 7923 7924 def p_ArgumentName(self, p): 7925 """ 7926 ArgumentName : IDENTIFIER 7927 | ArgumentNameKeyword 7928 """ 7929 p[0] = p[1] 7930 7931 def p_ArgumentNameKeyword(self, p): 7932 """ 7933 ArgumentNameKeyword : ASYNC 7934 | ATTRIBUTE 7935 | CALLBACK 7936 | CONST 7937 | CONSTRUCTOR 7938 | DELETER 7939 | DICTIONARY 7940 | ENUM 7941 | EXCEPTION 7942 | GETTER 7943 | INCLUDES 7944 | INHERIT 7945 | INTERFACE 7946 | ITERABLE 7947 | LEGACYCALLER 7948 | MAPLIKE 7949 | MIXIN 7950 | NAMESPACE 7951 | PARTIAL 7952 | READONLY 7953 | REQUIRED 7954 | SERIALIZER 7955 | SETLIKE 7956 | SETTER 7957 | STATIC 7958 | STRINGIFIER 7959 | TYPEDEF 7960 | UNRESTRICTED 7961 """ 7962 p[0] = p[1] 7963 7964 def p_AttributeName(self, p): 7965 """ 7966 AttributeName : IDENTIFIER 7967 | AttributeNameKeyword 7968 """ 7969 p[0] = p[1] 7970 7971 def p_AttributeNameKeyword(self, p): 7972 """ 7973 AttributeNameKeyword : ASYNC 7974 | REQUIRED 7975 """ 7976 p[0] = p[1] 7977 7978 def p_Ellipsis(self, p): 7979 """ 7980 Ellipsis : ELLIPSIS 7981 """ 7982 p[0] = True 7983 7984 def p_EllipsisEmpty(self, p): 7985 """ 7986 Ellipsis : 7987 """ 7988 p[0] = False 7989 7990 def p_ExceptionMember(self, p): 7991 """ 7992 ExceptionMember : Const 7993 | ExceptionField 7994 """ 7995 pass 7996 7997 def p_ExceptionField(self, p): 7998 """ 7999 ExceptionField : Type IDENTIFIER SEMICOLON 8000 """ 8001 pass 8002 8003 def p_ExtendedAttributeList(self, p): 8004 """ 8005 ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET 8006 """ 8007 p[0] = [p[2]] 8008 if p[3]: 8009 p[0].extend(p[3]) 8010 8011 def p_ExtendedAttributeListEmpty(self, p): 8012 """ 8013 ExtendedAttributeList : 8014 """ 8015 p[0] = [] 8016 8017 def p_ExtendedAttribute(self, p): 8018 """ 8019 ExtendedAttribute : ExtendedAttributeNoArgs 8020 | ExtendedAttributeArgList 8021 | ExtendedAttributeIdent 8022 | ExtendedAttributeNamedArgList 8023 | ExtendedAttributeIdentList 8024 """ 8025 p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1]) 8026 8027 def p_ExtendedAttributeEmpty(self, p): 8028 """ 8029 ExtendedAttribute : 8030 """ 8031 pass 8032 8033 def p_ExtendedAttributes(self, p): 8034 """ 8035 ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes 8036 """ 8037 p[0] = [p[2]] if p[2] else [] 8038 p[0].extend(p[3]) 8039 8040 def p_ExtendedAttributesEmpty(self, p): 8041 """ 8042 ExtendedAttributes : 8043 """ 8044 p[0] = [] 8045 8046 def p_Other(self, p): 8047 """ 8048 Other : INTEGER 8049 | FLOATLITERAL 8050 | IDENTIFIER 8051 | STRING 8052 | OTHER 8053 | ELLIPSIS 8054 | COLON 8055 | SCOPE 8056 | SEMICOLON 8057 | LT 8058 | EQUALS 8059 | GT 8060 | QUESTIONMARK 8061 | DOMSTRING 8062 | BYTESTRING 8063 | USVSTRING 8064 | UTF8STRING 8065 | JSSTRING 8066 | PROMISE 8067 | ANY 8068 | BOOLEAN 8069 | BYTE 8070 | DOUBLE 8071 | FALSE 8072 | FLOAT 8073 | LONG 8074 | NULL 8075 | OBJECT 8076 | OCTET 8077 | OR 8078 | OPTIONAL 8079 | RECORD 8080 | SEQUENCE 8081 | SHORT 8082 | SYMBOL 8083 | TRUE 8084 | UNSIGNED 8085 | VOID 8086 | ArgumentNameKeyword 8087 """ 8088 pass 8089 8090 def p_OtherOrComma(self, p): 8091 """ 8092 OtherOrComma : Other 8093 | COMMA 8094 """ 8095 pass 8096 8097 def p_TypeSingleType(self, p): 8098 """ 8099 Type : SingleType 8100 """ 8101 p[0] = p[1] 8102 8103 def p_TypeUnionType(self, p): 8104 """ 8105 Type : UnionType Null 8106 """ 8107 p[0] = self.handleNullable(p[1], p[2]) 8108 8109 def p_TypeWithExtendedAttributes(self, p): 8110 """ 8111 TypeWithExtendedAttributes : ExtendedAttributeList Type 8112 """ 8113 p[0] = p[2].withExtendedAttributes(p[1]) 8114 8115 def p_SingleTypeDistinguishableType(self, p): 8116 """ 8117 SingleType : DistinguishableType 8118 """ 8119 p[0] = p[1] 8120 8121 def p_SingleTypeAnyType(self, p): 8122 """ 8123 SingleType : ANY 8124 """ 8125 p[0] = BuiltinTypes[IDLBuiltinType.Types.any] 8126 8127 # Note: Promise<void> is allowed, so we want to parametrize on ReturnType, 8128 # not Type. Promise types can't be null, hence no "Null" in there. 8129 def p_SingleTypePromiseType(self, p): 8130 """ 8131 SingleType : PROMISE LT ReturnType GT 8132 """ 8133 p[0] = IDLPromiseType(self.getLocation(p, 1), p[3]) 8134 8135 def p_UnionType(self, p): 8136 """ 8137 UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN 8138 """ 8139 types = [p[2], p[4]] 8140 types.extend(p[5]) 8141 p[0] = IDLUnionType(self.getLocation(p, 1), types) 8142 8143 def p_UnionMemberTypeDistinguishableType(self, p): 8144 """ 8145 UnionMemberType : ExtendedAttributeList DistinguishableType 8146 """ 8147 p[0] = p[2].withExtendedAttributes(p[1]) 8148 8149 def p_UnionMemberType(self, p): 8150 """ 8151 UnionMemberType : UnionType Null 8152 """ 8153 p[0] = self.handleNullable(p[1], p[2]) 8154 8155 def p_UnionMemberTypes(self, p): 8156 """ 8157 UnionMemberTypes : OR UnionMemberType UnionMemberTypes 8158 """ 8159 p[0] = [p[2]] 8160 p[0].extend(p[3]) 8161 8162 def p_UnionMemberTypesEmpty(self, p): 8163 """ 8164 UnionMemberTypes : 8165 """ 8166 p[0] = [] 8167 8168 def p_DistinguishableType(self, p): 8169 """ 8170 DistinguishableType : PrimitiveType Null 8171 | ARRAYBUFFER Null 8172 | READABLESTREAM Null 8173 | OBJECT Null 8174 """ 8175 if p[1] == "object": 8176 type = BuiltinTypes[IDLBuiltinType.Types.object] 8177 elif p[1] == "ArrayBuffer": 8178 type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] 8179 elif p[1] == "ReadableStream": 8180 type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream] 8181 else: 8182 type = BuiltinTypes[p[1]] 8183 8184 p[0] = self.handleNullable(type, p[2]) 8185 8186 def p_DistinguishableTypeStringType(self, p): 8187 """ 8188 DistinguishableType : StringType Null 8189 """ 8190 p[0] = self.handleNullable(p[1], p[2]) 8191 8192 def p_DistinguishableTypeSequenceType(self, p): 8193 """ 8194 DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null 8195 """ 8196 innerType = p[3] 8197 type = IDLSequenceType(self.getLocation(p, 1), innerType) 8198 p[0] = self.handleNullable(type, p[5]) 8199 8200 def p_DistinguishableTypeRecordType(self, p): 8201 """ 8202 DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null 8203 """ 8204 keyType = p[3] 8205 valueType = p[5] 8206 type = IDLRecordType(self.getLocation(p, 1), keyType, valueType) 8207 p[0] = self.handleNullable(type, p[7]) 8208 8209 def p_DistinguishableTypeScopedName(self, p): 8210 """ 8211 DistinguishableType : ScopedName Null 8212 """ 8213 assert isinstance(p[1], IDLUnresolvedIdentifier) 8214 8215 if p[1].name == "Promise": 8216 raise WebIDLError( 8217 "Promise used without saying what it's " "parametrized over", 8218 [self.getLocation(p, 1)], 8219 ) 8220 8221 type = None 8222 8223 try: 8224 if self.globalScope()._lookupIdentifier(p[1]): 8225 obj = self.globalScope()._lookupIdentifier(p[1]) 8226 assert not obj.isType() 8227 if obj.isTypedef(): 8228 type = IDLTypedefType( 8229 self.getLocation(p, 1), obj.innerType, obj.identifier.name 8230 ) 8231 elif obj.isCallback() and not obj.isInterface(): 8232 type = IDLCallbackType(self.getLocation(p, 1), obj) 8233 else: 8234 type = IDLWrapperType(self.getLocation(p, 1), p[1]) 8235 p[0] = self.handleNullable(type, p[2]) 8236 return 8237 except: 8238 pass 8239 8240 type = IDLUnresolvedType(self.getLocation(p, 1), p[1]) 8241 p[0] = self.handleNullable(type, p[2]) 8242 8243 def p_ConstType(self, p): 8244 """ 8245 ConstType : PrimitiveType 8246 """ 8247 p[0] = BuiltinTypes[p[1]] 8248 8249 def p_ConstTypeIdentifier(self, p): 8250 """ 8251 ConstType : IDENTIFIER 8252 """ 8253 identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) 8254 8255 p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier) 8256 8257 def p_PrimitiveTypeUint(self, p): 8258 """ 8259 PrimitiveType : UnsignedIntegerType 8260 """ 8261 p[0] = p[1] 8262 8263 def p_PrimitiveTypeBoolean(self, p): 8264 """ 8265 PrimitiveType : BOOLEAN 8266 """ 8267 p[0] = IDLBuiltinType.Types.boolean 8268 8269 def p_PrimitiveTypeByte(self, p): 8270 """ 8271 PrimitiveType : BYTE 8272 """ 8273 p[0] = IDLBuiltinType.Types.byte 8274 8275 def p_PrimitiveTypeOctet(self, p): 8276 """ 8277 PrimitiveType : OCTET 8278 """ 8279 p[0] = IDLBuiltinType.Types.octet 8280 8281 def p_PrimitiveTypeFloat(self, p): 8282 """ 8283 PrimitiveType : FLOAT 8284 """ 8285 p[0] = IDLBuiltinType.Types.float 8286 8287 def p_PrimitiveTypeUnrestictedFloat(self, p): 8288 """ 8289 PrimitiveType : UNRESTRICTED FLOAT 8290 """ 8291 p[0] = IDLBuiltinType.Types.unrestricted_float 8292 8293 def p_PrimitiveTypeDouble(self, p): 8294 """ 8295 PrimitiveType : DOUBLE 8296 """ 8297 p[0] = IDLBuiltinType.Types.double 8298 8299 def p_PrimitiveTypeUnrestictedDouble(self, p): 8300 """ 8301 PrimitiveType : UNRESTRICTED DOUBLE 8302 """ 8303 p[0] = IDLBuiltinType.Types.unrestricted_double 8304 8305 def p_StringType(self, p): 8306 """ 8307 StringType : BuiltinStringType 8308 """ 8309 p[0] = BuiltinTypes[p[1]] 8310 8311 def p_BuiltinStringTypeDOMString(self, p): 8312 """ 8313 BuiltinStringType : DOMSTRING 8314 """ 8315 p[0] = IDLBuiltinType.Types.domstring 8316 8317 def p_BuiltinStringTypeBytestring(self, p): 8318 """ 8319 BuiltinStringType : BYTESTRING 8320 """ 8321 p[0] = IDLBuiltinType.Types.bytestring 8322 8323 def p_BuiltinStringTypeUSVString(self, p): 8324 """ 8325 BuiltinStringType : USVSTRING 8326 """ 8327 p[0] = IDLBuiltinType.Types.usvstring 8328 8329 def p_BuiltinStringTypeUTF8String(self, p): 8330 """ 8331 BuiltinStringType : UTF8STRING 8332 """ 8333 p[0] = IDLBuiltinType.Types.utf8string 8334 8335 def p_BuiltinStringTypeJSString(self, p): 8336 """ 8337 BuiltinStringType : JSSTRING 8338 """ 8339 p[0] = IDLBuiltinType.Types.jsstring 8340 8341 def p_UnsignedIntegerTypeUnsigned(self, p): 8342 """ 8343 UnsignedIntegerType : UNSIGNED IntegerType 8344 """ 8345 # Adding one to a given signed integer type gets you the unsigned type: 8346 p[0] = p[2] + 1 8347 8348 def p_UnsignedIntegerType(self, p): 8349 """ 8350 UnsignedIntegerType : IntegerType 8351 """ 8352 p[0] = p[1] 8353 8354 def p_IntegerTypeShort(self, p): 8355 """ 8356 IntegerType : SHORT 8357 """ 8358 p[0] = IDLBuiltinType.Types.short 8359 8360 def p_IntegerTypeLong(self, p): 8361 """ 8362 IntegerType : LONG OptionalLong 8363 """ 8364 if p[2]: 8365 p[0] = IDLBuiltinType.Types.long_long 8366 else: 8367 p[0] = IDLBuiltinType.Types.long 8368 8369 def p_OptionalLong(self, p): 8370 """ 8371 OptionalLong : LONG 8372 """ 8373 p[0] = True 8374 8375 def p_OptionalLongEmpty(self, p): 8376 """ 8377 OptionalLong : 8378 """ 8379 p[0] = False 8380 8381 def p_Null(self, p): 8382 """ 8383 Null : QUESTIONMARK 8384 | 8385 """ 8386 if len(p) > 1: 8387 p[0] = self.getLocation(p, 1) 8388 else: 8389 p[0] = None 8390 8391 def p_ReturnTypeType(self, p): 8392 """ 8393 ReturnType : Type 8394 """ 8395 p[0] = p[1] 8396 8397 def p_ReturnTypeVoid(self, p): 8398 """ 8399 ReturnType : VOID 8400 """ 8401 p[0] = BuiltinTypes[IDLBuiltinType.Types.void] 8402 8403 def p_ScopedName(self, p): 8404 """ 8405 ScopedName : AbsoluteScopedName 8406 | RelativeScopedName 8407 """ 8408 p[0] = p[1] 8409 8410 def p_AbsoluteScopedName(self, p): 8411 """ 8412 AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts 8413 """ 8414 assert False 8415 pass 8416 8417 def p_RelativeScopedName(self, p): 8418 """ 8419 RelativeScopedName : IDENTIFIER ScopedNameParts 8420 """ 8421 assert not p[2] # Not implemented! 8422 8423 p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) 8424 8425 def p_ScopedNameParts(self, p): 8426 """ 8427 ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts 8428 """ 8429 assert False 8430 pass 8431 8432 def p_ScopedNamePartsEmpty(self, p): 8433 """ 8434 ScopedNameParts : 8435 """ 8436 p[0] = None 8437 8438 def p_ExtendedAttributeNoArgs(self, p): 8439 """ 8440 ExtendedAttributeNoArgs : IDENTIFIER 8441 """ 8442 p[0] = (p[1],) 8443 8444 def p_ExtendedAttributeArgList(self, p): 8445 """ 8446 ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN 8447 """ 8448 p[0] = (p[1], p[3]) 8449 8450 def p_ExtendedAttributeIdent(self, p): 8451 """ 8452 ExtendedAttributeIdent : IDENTIFIER EQUALS STRING 8453 | IDENTIFIER EQUALS IDENTIFIER 8454 """ 8455 p[0] = (p[1], p[3]) 8456 8457 def p_ExtendedAttributeNamedArgList(self, p): 8458 """ 8459 ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN 8460 """ 8461 p[0] = (p[1], p[3], p[5]) 8462 8463 def p_ExtendedAttributeIdentList(self, p): 8464 """ 8465 ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN 8466 """ 8467 p[0] = (p[1], p[4]) 8468 8469 def p_IdentifierList(self, p): 8470 """ 8471 IdentifierList : IDENTIFIER Identifiers 8472 """ 8473 idents = list(p[2]) 8474 # This is only used for identifier-list-valued extended attributes, and if 8475 # we're going to restrict to IDENTIFIER here we should at least allow 8476 # escaping with leading '_' as usual for identifiers. 8477 ident = p[1] 8478 if ident[0] == "_": 8479 ident = ident[1:] 8480 idents.insert(0, ident) 8481 p[0] = idents 8482 8483 def p_IdentifiersList(self, p): 8484 """ 8485 Identifiers : COMMA IDENTIFIER Identifiers 8486 """ 8487 idents = list(p[3]) 8488 # This is only used for identifier-list-valued extended attributes, and if 8489 # we're going to restrict to IDENTIFIER here we should at least allow 8490 # escaping with leading '_' as usual for identifiers. 8491 ident = p[2] 8492 if ident[0] == "_": 8493 ident = ident[1:] 8494 idents.insert(0, ident) 8495 p[0] = idents 8496 8497 def p_IdentifiersEmpty(self, p): 8498 """ 8499 Identifiers : 8500 """ 8501 p[0] = [] 8502 8503 def p_error(self, p): 8504 if not p: 8505 raise WebIDLError( 8506 "Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", 8507 [self._filename], 8508 ) 8509 else: 8510 raise WebIDLError( 8511 "invalid syntax", 8512 [Location(self.lexer, p.lineno, p.lexpos, self._filename)], 8513 ) 8514 8515 def __init__(self, outputdir="", lexer=None): 8516 Tokenizer.__init__(self, outputdir, lexer) 8517 8518 logger = SqueakyCleanLogger() 8519 try: 8520 self.parser = yacc.yacc( 8521 module=self, 8522 outputdir=outputdir, 8523 errorlog=logger, 8524 write_tables=False, 8525 # Pickling the grammar is a speedup in 8526 # some cases (older Python?) but a 8527 # significant slowdown in others. 8528 # We're not pickling for now, until it 8529 # becomes a speedup again. 8530 # , picklefile='WebIDLGrammar.pkl' 8531 ) 8532 finally: 8533 logger.reportGrammarErrors() 8534 8535 self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None) 8536 8537 self._installBuiltins(self._globalScope) 8538 self._productions = [] 8539 8540 self._filename = "<builtin>" 8541 self.lexer.input(Parser._builtins) 8542 self._filename = None 8543 8544 self.parser.parse(lexer=self.lexer, tracking=True) 8545 8546 def _installBuiltins(self, scope): 8547 assert isinstance(scope, IDLScope) 8548 8549 # range omits the last value. 8550 for x in range( 8551 IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1 8552 ): 8553 builtin = BuiltinTypes[x] 8554 name = builtin.name 8555 typedef = IDLTypedef( 8556 BuiltinLocation("<builtin type>"), scope, builtin, name 8557 ) 8558 8559 @staticmethod 8560 def handleNullable(type, questionMarkLocation): 8561 if questionMarkLocation is not None: 8562 type = IDLNullableType(questionMarkLocation, type) 8563 8564 return type 8565 8566 def parse(self, t, filename=None): 8567 self.lexer.input(t) 8568 8569 # for tok in iter(self.lexer.token, None): 8570 # print tok 8571 8572 self._filename = filename 8573 self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True)) 8574 self._filename = None 8575 8576 def finish(self): 8577 # If we have interfaces that are iterable, create their 8578 # iterator interfaces and add them to the productions array. 8579 interfaceStatements = [] 8580 for p in self._productions: 8581 if isinstance(p, IDLInterface): 8582 interfaceStatements.append(p) 8583 8584 iterableIteratorIface = None 8585 for iface in interfaceStatements: 8586 iterable = None 8587 # We haven't run finish() on the interface yet, so we don't know 8588 # whether our interface is maplike/setlike/iterable or not. This 8589 # means we have to loop through the members to see if we have an 8590 # iterable member. 8591 for m in iface.members: 8592 if isinstance(m, IDLIterable): 8593 iterable = m 8594 break 8595 if iterable and iterable.isPairIterator(): 8596 8597 def simpleExtendedAttr(str): 8598 return IDLExtendedAttribute(iface.location, (str,)) 8599 8600 nextMethod = IDLMethod( 8601 iface.location, 8602 IDLUnresolvedIdentifier(iface.location, "next"), 8603 BuiltinTypes[IDLBuiltinType.Types.object], 8604 [], 8605 ) 8606 nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")]) 8607 itr_ident = IDLUnresolvedIdentifier( 8608 iface.location, iface.identifier.name + "Iterator" 8609 ) 8610 classNameOverride = iface.identifier.name + " Iterator" 8611 itr_iface = IDLInterface( 8612 iface.location, 8613 self.globalScope(), 8614 itr_ident, 8615 None, 8616 [nextMethod], 8617 isKnownNonPartial=True, 8618 classNameOverride=classNameOverride, 8619 ) 8620 itr_iface.addExtendedAttributes( 8621 [simpleExtendedAttr("LegacyNoInterfaceObject")] 8622 ) 8623 # Make sure the exposure set for the iterator interface is the 8624 # same as the exposure set for the iterable interface, because 8625 # we're going to generate methods on the iterable that return 8626 # instances of the iterator. 8627 itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames) 8628 # Always append generated iterable interfaces after the 8629 # interface they're a member of, otherwise nativeType generation 8630 # won't work correctly. 8631 itr_iface.iterableInterface = iface 8632 self._productions.append(itr_iface) 8633 iterable.iteratorType = IDLWrapperType(iface.location, itr_iface) 8634 8635 # Make sure we finish IDLIncludesStatements before we finish the 8636 # IDLInterfaces. 8637 # XXX khuey hates this bit and wants to nuke it from orbit. 8638 includesStatements = [ 8639 p for p in self._productions if isinstance(p, IDLIncludesStatement) 8640 ] 8641 otherStatements = [ 8642 p for p in self._productions if not isinstance(p, IDLIncludesStatement) 8643 ] 8644 for production in includesStatements: 8645 production.finish(self.globalScope()) 8646 for production in otherStatements: 8647 production.finish(self.globalScope()) 8648 8649 # Do any post-finish validation we need to do 8650 for production in self._productions: 8651 production.validate() 8652 8653 # De-duplicate self._productions, without modifying its order. 8654 seen = set() 8655 result = [] 8656 for p in self._productions: 8657 if p not in seen: 8658 seen.add(p) 8659 result.append(p) 8660 return result 8661 8662 def reset(self): 8663 return Parser(lexer=self.lexer) 8664 8665 # Builtin IDL defined by WebIDL 8666 _builtins = """ 8667 typedef unsigned long long DOMTimeStamp; 8668 typedef (ArrayBufferView or ArrayBuffer) BufferSource; 8669 """ 8670 8671 8672def main(): 8673 # Parse arguments. 8674 from optparse import OptionParser 8675 8676 usageString = "usage: %prog [options] files" 8677 o = OptionParser(usage=usageString) 8678 o.add_option( 8679 "--cachedir", 8680 dest="cachedir", 8681 default=None, 8682 help="Directory in which to cache lex/parse tables.", 8683 ) 8684 o.add_option( 8685 "--verbose-errors", 8686 action="store_true", 8687 default=False, 8688 help="When an error happens, display the Python traceback.", 8689 ) 8690 (options, args) = o.parse_args() 8691 8692 if len(args) < 1: 8693 o.error(usageString) 8694 8695 fileList = args 8696 baseDir = os.getcwd() 8697 8698 # Parse the WebIDL. 8699 parser = Parser(options.cachedir) 8700 try: 8701 for filename in fileList: 8702 fullPath = os.path.normpath(os.path.join(baseDir, filename)) 8703 f = open(fullPath, "rb") 8704 lines = f.readlines() 8705 f.close() 8706 print(fullPath) 8707 parser.parse("".join(lines), fullPath) 8708 parser.finish() 8709 except WebIDLError as e: 8710 if options.verbose_errors: 8711 traceback.print_exc() 8712 else: 8713 print(e) 8714 8715 8716if __name__ == "__main__": 8717 main() 8718