Configuration.py (60393B)
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 file, 3 # You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import itertools 6 import os 7 from collections import defaultdict 8 9 from WebIDL import IDLIncludesStatement, IDLInterface, IDLTypedef 10 11 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" 12 13 14 def toStringBool(arg): 15 """ 16 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false) 17 """ 18 return str(not not arg).lower() 19 20 21 class DescriptorProvider: 22 """ 23 A way of getting descriptors for interface names. Subclasses must 24 have a getDescriptor method callable with the interface name only. 25 26 Subclasses must also have a getConfig() method that returns a 27 Configuration. 28 """ 29 30 def __init__(self): 31 pass 32 33 34 def isChildPath(path, basePath): 35 path = os.path.normpath(path) 36 return os.path.commonprefix((path, basePath)) == basePath 37 38 39 class Configuration(DescriptorProvider): 40 """ 41 Represents global configuration state based on IDL parse data and 42 the configuration file. 43 """ 44 45 unionTypedefs: list[IDLTypedef] 46 47 class IDLAttrGetterOrSetterTemplate: 48 class TemplateAdditionalArg: 49 def __init__(self, type, name, value=None): 50 self.type = type 51 self.name = name 52 self.value = value 53 54 def __init__(self, template, getter, setter, argument, attrName): 55 self.descriptor = None 56 self.usedInOtherInterfaces = False 57 self.getter = getter 58 self.setter = setter 59 self.argument = ( 60 Configuration.IDLAttrGetterOrSetterTemplate.TemplateAdditionalArg( 61 *argument 62 ) 63 ) 64 self.attrNameString = attrName 65 self.attr = None 66 67 class TemplateIDLAttribute: 68 def __init__(self, attr): 69 assert attr.isAttr() 70 assert not attr.isMaplikeOrSetlikeAttr() 71 assert not attr.slotIndices 72 73 self.identifier = attr.identifier 74 self.type = attr.type 75 self.extendedAttributes = attr.getExtendedAttributes() 76 self.slotIndices = None 77 78 def getExtendedAttribute(self, name): 79 return self.extendedAttributes.get(name) 80 81 def isAttr(self): 82 return True 83 84 def isMaplikeOrSetlikeAttr(self): 85 return False 86 87 def isMethod(self): 88 return False 89 90 def isStatic(self): 91 return False 92 93 def __init__(self, filename, webRoots, parseData, generatedEvents=[]): 94 DescriptorProvider.__init__(self) 95 96 # Read the configuration file. 97 glbl = {} 98 exec(open(filename, encoding="utf-8").read(), glbl) 99 config = glbl["DOMInterfaces"] 100 101 self.attributeTemplates = dict() 102 attributeTemplatesByInterface = dict() 103 for interface, templates in glbl["TemplatedAttributes"].items(): 104 for template in templates: 105 name = template.get("template") 106 t = Configuration.IDLAttrGetterOrSetterTemplate(**template) 107 self.attributeTemplates[name] = t 108 attributeTemplatesByInterface.setdefault(interface, list()).append(t) 109 110 webRoots = tuple(map(os.path.normpath, webRoots)) 111 112 def isInWebIDLRoot(path): 113 return any(isChildPath(path, root) for root in webRoots) 114 115 # Build descriptors for all the interfaces we have in the parse data. 116 # This allows callers to specify a subset of interfaces by filtering 117 # |parseData|. 118 self.descriptors = [] 119 self.interfaces = {} 120 self.descriptorsByName = {} 121 self.dictionariesByName = {} 122 self.generatedEvents = generatedEvents 123 self.maxProtoChainLength = 0 124 for thing in parseData: 125 if isinstance(thing, IDLIncludesStatement): 126 # Our build system doesn't support dep build involving 127 # addition/removal of "includes" statements that appear in a 128 # different .webidl file than their LHS interface. Make sure we 129 # don't have any of those. See similar block below for partial 130 # interfaces! 131 if thing.interface.filename != thing.filename: 132 raise TypeError( 133 "The binding build system doesn't really support " 134 "'includes' statements which don't appear in the " 135 "file in which the left-hand side of the statement is " 136 "defined.\n" 137 "%s\n" 138 "%s" % (thing.location, thing.interface.location) 139 ) 140 141 assert not thing.isType() 142 143 if ( 144 not thing.isInterface() 145 and not thing.isNamespace() 146 and not thing.isInterfaceMixin() 147 ): 148 continue 149 # Our build system doesn't support dep builds involving 150 # addition/removal of partial interfaces/namespaces/mixins that 151 # appear in a different .webidl file than the 152 # interface/namespace/mixin they are extending. Make sure we don't 153 # have any of those. See similar block above for "includes" 154 # statements! 155 if not thing.isExternal(): 156 for partial in thing.getPartials(): 157 if partial.filename != thing.filename: 158 raise TypeError( 159 "The binding build system doesn't really support " 160 "partial interfaces/namespaces/mixins which don't " 161 "appear in the file in which the " 162 "interface/namespace/mixin they are extending is " 163 "defined. Don't do this.\n" 164 "%s\n" 165 "%s" % (partial.location, thing.location) 166 ) 167 168 # The rest of the logic doesn't apply to mixins. 169 if thing.isInterfaceMixin(): 170 continue 171 172 iface = thing 173 if not iface.isExternal(): 174 if not ( 175 iface.getExtendedAttribute("ChromeOnly") 176 or iface.getExtendedAttribute("Func") 177 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"] 178 or not iface.hasInterfaceObject() 179 or isInWebIDLRoot(iface.filename) 180 ): 181 raise TypeError( 182 "Interfaces which are exposed to the web may only be " 183 "defined in a DOM WebIDL root %r. Consider marking " 184 "the interface [ChromeOnly] or " 185 "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] " 186 "if you do not want it exposed to the web.\n" 187 "%s" % (webRoots, iface.location) 188 ) 189 190 self.interfaces[iface.identifier.name] = iface 191 192 entry = config.pop(iface.identifier.name, {}) 193 assert not isinstance(entry, list) 194 195 desc = Descriptor( 196 self, 197 iface, 198 entry, 199 attributeTemplatesByInterface.get(iface.identifier.name), 200 ) 201 self.descriptors.append(desc) 202 # Setting up descriptorsByName while iterating through interfaces 203 # means we can get the nativeType of iterable interfaces without 204 # having to do multiple loops. 205 assert desc.interface.identifier.name not in self.descriptorsByName 206 self.descriptorsByName[desc.interface.identifier.name] = desc 207 208 if len(config) > 0: 209 raise NoSuchDescriptorError( 210 "Bindings.conf contains entries for " 211 + str(list(config)) 212 + " that aren't declared as interfaces in the .webidl files." 213 ) 214 215 # Keep the descriptor list sorted for determinism. 216 self.descriptors.sort(key=lambda x: x.name) 217 218 self.descriptorsByFile = {} 219 for d in self.descriptors: 220 self.descriptorsByFile.setdefault(d.interface.filename, []).append(d) 221 222 self.enums = [e for e in parseData if e.isEnum()] 223 224 self.dictionaries = [d for d in parseData if d.isDictionary()] 225 self.dictionariesByName = {d.identifier.name: d for d in self.dictionaries} 226 227 self.callbacks = [ 228 c for c in parseData if c.isCallback() and not c.isInterface() 229 ] 230 231 self.unionTypedefs = [ 232 t for t in parseData if t.isTypedef() and t.innerType.isUnion() 233 ] 234 235 # Dictionary mapping from a union type name to a set of filenames where 236 # union types with that name are used. 237 self.filenamesPerUnion = defaultdict(set) 238 239 # Dictionary mapping from a filename to a list of types for 240 # the union types used in that file. If a union type is used 241 # in multiple files then it will be added to the list for the 242 # None key. Note that the list contains a type for every use 243 # of a union type, so there can be multiple entries with union 244 # types that have the same name. 245 self.unionsPerFilename = defaultdict(list) 246 247 def addUnion(t): 248 filenamesForUnion = self.filenamesPerUnion[t.name] 249 if t.filename not in filenamesForUnion: 250 # We have a to be a bit careful: some of our built-in 251 # typedefs are for unions, and those unions end up with 252 # "<unknown>" as the filename. If that happens, we don't 253 # want to try associating this union with one particular 254 # filename, since there isn't one to associate it with, 255 # really. 256 if t.filename == "<unknown>": 257 uniqueFilenameForUnion = None 258 elif len(filenamesForUnion) == 0: 259 # This is the first file that we found a union with this 260 # name in, record the union as part of the file. 261 uniqueFilenameForUnion = t.filename 262 else: 263 # We already found a file that contains a union with 264 # this name. 265 if len(filenamesForUnion) == 1: 266 # This is the first time we found a union with this 267 # name in another file. 268 for f in filenamesForUnion: 269 # Filter out unions with this name from the 270 # unions for the file where we previously found 271 # them. 272 unionsForFilename = [ 273 u for u in self.unionsPerFilename[f] if u.name != t.name 274 ] 275 if len(unionsForFilename) == 0: 276 del self.unionsPerFilename[f] 277 else: 278 self.unionsPerFilename[f] = unionsForFilename 279 # Unions with this name appear in multiple files, record 280 # the filename as None, so that we can detect that. 281 uniqueFilenameForUnion = None 282 self.unionsPerFilename[uniqueFilenameForUnion].append(t) 283 filenamesForUnion.add(t.filename) 284 285 def addUnions(t): 286 t = findInnermostType(t) 287 if t.isUnion(): 288 addUnion(t) 289 for m in t.flatMemberTypes: 290 addUnions(m) 291 292 for t, _ in getAllTypes(self.descriptors, self.dictionaries, self.callbacks): 293 addUnions(t) 294 295 for d in getDictionariesConvertedToJS( 296 self.descriptors, self.dictionaries, self.callbacks 297 ): 298 d.needsConversionToJS = True 299 300 for d in getDictionariesConvertedFromJS( 301 self.descriptors, self.dictionaries, self.callbacks 302 ): 303 d.needsConversionFromJS = True 304 305 # Collect all the global names exposed on a Window object (to implement 306 # the hash for looking up these names when resolving a property). 307 self.windowGlobalNames = [] 308 for desc in self.getDescriptors(registersGlobalNamesOnWindow=True): 309 self.windowGlobalNames.append((desc.name, desc)) 310 self.windowGlobalNames.extend( 311 (n.identifier.name, desc) for n in desc.interface.legacyFactoryFunctions 312 ) 313 self.windowGlobalNames.extend( 314 (n, desc) for n in desc.interface.legacyWindowAliases 315 ) 316 317 # Collect a sorted list of strings that we want to concatenate into 318 # one big string and a dict mapping each string to its offset in the 319 # concatenated string. 320 321 # We want the names of all the interfaces with a prototype (for 322 # implementing @@toStringTag). 323 names = set( 324 d.interface.getClassName() 325 for d in self.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True) 326 ) 327 names.update( 328 d.interface.getClassName() 329 for d in self.getDescriptors(hasOrdinaryObjectPrototype=True) 330 ) 331 332 # Now also add the names from windowGlobalNames, we need them for the 333 # perfect hash that we build for these. 334 names.update(n[0] for n in self.windowGlobalNames) 335 336 # Sorting is not strictly necessary, but makes the generated code a bit 337 # more readable. 338 names = sorted(names) 339 340 # We can't rely on being able to pass initial=0 to itertools.accumulate 341 # because it was only added in version 3.8, so define an accumulate 342 # function that chains the initial value into the iterator. 343 def accumulate(iterable, initial): 344 return itertools.accumulate(itertools.chain([initial], iterable)) 345 346 # Calculate the offset of each name in the concatenated string. Note that 347 # we need to add 1 to the length to account for the null terminating each 348 # name. 349 offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0) 350 self.namesStringOffsets = list(zip(names, offsets)) 351 352 allTemplatedAttributes = ( 353 (m, d) 354 for d in self.descriptors 355 if not d.interface.isExternal() 356 for m in d.interface.members 357 if m.isAttr() and m.getExtendedAttribute("BindingTemplate") is not None 358 ) 359 # attributesPerTemplate will have the template names as keys, and a 360 # list of tuples as values. Every tuple contains an IDLAttribute and a 361 # descriptor. 362 attributesPerTemplate = dict() 363 for m, d in allTemplatedAttributes: 364 t = m.getExtendedAttribute("BindingTemplate") 365 if isinstance(t[0], list): 366 t = t[0] 367 l = attributesPerTemplate.setdefault(t[0], list()) 368 # We want the readonly attributes last, because we use the first 369 # attribute in the list as the canonical attribute for the 370 # template, and if there are any writable attributes the 371 # template should have support for that. 372 if not m.readonly: 373 l.insert(0, (m, d)) 374 else: 375 l.append((m, d)) 376 377 for name, attributes in attributesPerTemplate.items(): 378 # We use the first attribute to generate a canonical implementation 379 # of getter and setter. 380 firstAttribute, firstDescriptor = attributes[0] 381 template = self.attributeTemplates.get(name) 382 if template is None: 383 raise TypeError( 384 "Unknown BindingTemplate with name %s for %s on %s" 385 % ( 386 name, 387 firstAttribute.identifier.name, 388 firstDescriptor.interface.identifier.name, 389 ) 390 ) 391 392 # This mimics a real IDL attribute for templated bindings. 393 394 template.attr = Configuration.TemplateIDLAttribute(firstAttribute) 395 396 def filterExtendedAttributes(extendedAttributes): 397 # These are the extended attributes that we allow to have 398 # different values among all attributes that use the same 399 # template. 400 ignoredAttributes = { 401 "BindingTemplate", 402 "BindingAlias", 403 "ChromeOnly", 404 "Pure", 405 "Pref", 406 "Func", 407 "Throws", 408 "GetterThrows", 409 "SetterThrows", 410 } 411 return dict( 412 filter( 413 lambda i: i[0] not in ignoredAttributes, 414 extendedAttributes.items(), 415 ) 416 ) 417 418 firstExtAttrs = filterExtendedAttributes( 419 firstAttribute.getExtendedAttributes() 420 ) 421 422 for a, d in attributes: 423 # We want to make sure all getters or setters grouped by a 424 # template have the same WebIDL signatures, so make sure 425 # their types are the same. 426 if template.attr.type != a.type: 427 raise TypeError( 428 "%s on %s and %s on %s have different type, but they're using the same template %s." 429 % ( 430 firstAttribute.identifier.name, 431 firstDescriptor.interface.identifier.name, 432 a.identifier.name, 433 d.interface.identifier.name, 434 name, 435 ) 436 ) 437 438 extAttrs = filterExtendedAttributes(a.getExtendedAttributes()) 439 if template.attr.extendedAttributes != extAttrs: 440 for k in extAttrs.keys() - firstExtAttrs.keys(): 441 raise TypeError( 442 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s." 443 % ( 444 a.identifier.name, 445 d.interface.identifier.name, 446 k, 447 firstAttribute.identifier.name, 448 firstDescriptor.interface.identifier.name, 449 name, 450 ) 451 ) 452 for k in firstExtAttrs.keys() - extAttrs.keys(): 453 raise TypeError( 454 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s." 455 % ( 456 firstAttribute.identifier.name, 457 firstDescriptor.interface.identifier.name, 458 k, 459 a.identifier.name, 460 d.interface.identifier.name, 461 name, 462 ) 463 ) 464 for k, v in firstExtAttrs.items(): 465 if extAttrs[k] != v: 466 raise TypeError( 467 "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s." 468 % ( 469 firstAttribute.identifier.name, 470 firstDescriptor.interface.identifier.name, 471 a.identifier.name, 472 d.interface.identifier.name, 473 k, 474 name, 475 ) 476 ) 477 478 def sameThrows(getter=False, setter=False): 479 extAttrs1 = firstDescriptor.getExtendedAttributes( 480 firstAttribute, getter=getter, setter=setter 481 ) 482 extAttrs2 = d.getExtendedAttributes(a, getter=getter, setter=setter) 483 return ("needsErrorResult" in extAttrs1) == ( 484 "needsErrorResult" in extAttrs2 485 ) 486 487 if not sameThrows(getter=True) or ( 488 not a.readonly and not sameThrows(setter=True) 489 ): 490 raise TypeError( 491 "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s." 492 % ( 493 firstAttribute.identifier.name, 494 firstDescriptor.interface.identifier.name, 495 a.identifier.name, 496 d.interface.identifier.name, 497 name, 498 ) 499 ) 500 501 for name, template in self.attributeTemplates.items(): 502 if template.attr is None: 503 print("Template %s is unused, please remove it." % name) 504 505 def getInterface(self, ifname): 506 return self.interfaces[ifname] 507 508 def getDescriptors(self, **filters): 509 """Gets the descriptors that match the given filters.""" 510 curr = self.descriptors 511 # Collect up our filters, because we may have a webIDLFile filter that 512 # we always want to apply first. 513 tofilter = [(lambda x: x.interface.isExternal(), False)] 514 for key, val in filters.items(): 515 if key == "webIDLFile": 516 # Special-case this part to make it fast, since most of our 517 # getDescriptors calls are conditioned on a webIDLFile. We may 518 # not have this key, in which case we have no descriptors 519 # either. 520 curr = self.descriptorsByFile.get(val, []) 521 continue 522 elif key == "hasInterfaceObject": 523 524 def getter(x): 525 return x.interface.hasInterfaceObject() 526 527 elif key == "hasInterfacePrototypeObject": 528 529 def getter(x): 530 return x.interface.hasInterfacePrototypeObject() 531 532 elif key == "hasInterfaceOrInterfacePrototypeObject": 533 534 def getter(x): 535 return x.hasInterfaceOrInterfacePrototypeObject() 536 537 elif key == "hasOrdinaryObjectPrototype": 538 539 def getter(x): 540 return x.hasOrdinaryObjectPrototype() 541 542 elif key == "isCallback": 543 544 def getter(x): 545 return x.interface.isCallback() 546 547 elif key == "isJSImplemented": 548 549 def getter(x): 550 return x.interface.isJSImplemented() 551 552 elif key == "isExposedInAnyWorker": 553 554 def getter(x): 555 return x.interface.isExposedInAnyWorker() 556 557 elif key == "isExposedInWorkerDebugger": 558 559 def getter(x): 560 return x.interface.isExposedInWorkerDebugger() 561 562 elif key == "isExposedInAnyWorklet": 563 564 def getter(x): 565 return x.interface.isExposedInAnyWorklet() 566 567 elif key == "isExposedInWindow": 568 569 def getter(x): 570 return x.interface.isExposedInWindow() 571 572 elif key == "isExposedInShadowRealms": 573 574 def getter(x): 575 return x.interface.isExposedInShadowRealms() 576 577 elif key == "isSerializable": 578 579 def getter(x): 580 return x.interface.isSerializable() 581 582 else: 583 # Have to watch out: just closing over "key" is not enough, 584 # since we're about to mutate its value 585 getter = (lambda attrName: lambda x: getattr(x, attrName))(key) 586 tofilter.append((getter, val)) 587 for f in tofilter: 588 curr = [x for x in curr if f[0](x) == f[1]] 589 return curr 590 591 def getEnums(self, webIDLFile): 592 return [e for e in self.enums if e.filename == webIDLFile] 593 594 def getDictionaries(self, webIDLFile): 595 return [d for d in self.dictionaries if d.filename == webIDLFile] 596 597 def getCallbacks(self, webIDLFile): 598 return [c for c in self.callbacks if c.filename == webIDLFile] 599 600 def getUnionTypedefs(self, webIDLFile): 601 return [t for t in self.unionTypedefs if t.filename == webIDLFile] 602 603 def getDescriptor(self, interfaceName): 604 """ 605 Gets the appropriate descriptor for the given interface name. 606 """ 607 # We may have optimized out this descriptor, but the chances of anyone 608 # asking about it are then slim. Put the check for that _after_ we've 609 # done our normal lookup. But that means we have to do our normal 610 # lookup in a way that will not throw if it fails. 611 d = self.descriptorsByName.get(interfaceName, None) 612 if d: 613 return d 614 615 raise NoSuchDescriptorError("For " + interfaceName + " found no matches") 616 617 def getConfig(self): 618 return self 619 620 def getDictionariesConvertibleToJS(self): 621 return [d for d in self.dictionaries if d.needsConversionToJS] 622 623 def getDictionariesConvertibleFromJS(self): 624 return [d for d in self.dictionaries if d.needsConversionFromJS] 625 626 def getDictionaryIfExists(self, dictionaryName): 627 return self.dictionariesByName.get(dictionaryName, None) 628 629 630 class NoSuchDescriptorError(TypeError): 631 def __init__(self, str): 632 TypeError.__init__(self, str) 633 634 635 def methodReturnsJSObject(method): 636 assert method.isMethod() 637 638 for signature in method.signatures(): 639 returnType = signature[0] 640 if returnType.isObject() or returnType.isSpiderMonkeyInterface(): 641 return True 642 643 return False 644 645 646 def MemberIsLegacyUnforgeable(member, descriptor): 647 # Note: "or" and "and" return either their LHS or RHS, not 648 # necessarily booleans. Make sure to return a boolean from this 649 # method, because callers will compare its return value to 650 # booleans. 651 return bool( 652 (member.isAttr() or member.isMethod()) 653 and not member.isStatic() 654 and ( 655 member.isLegacyUnforgeable() 656 or descriptor.interface.getExtendedAttribute("LegacyUnforgeable") 657 ) 658 ) 659 660 661 class Descriptor(DescriptorProvider): 662 """ 663 Represents a single descriptor for an interface. See Bindings.conf. 664 """ 665 666 def __init__(self, config, interface: IDLInterface, desc, attributeTemplates): 667 DescriptorProvider.__init__(self) 668 self.config = config 669 self.interface = interface 670 self.attributeTemplates = attributeTemplates 671 if self.attributeTemplates is not None: 672 for t in self.attributeTemplates: 673 t.descriptor = self 674 675 self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow() 676 677 if self.wantsXrays: 678 # We could try to restrict self.wantsXrayExpandoClass further. For 679 # example, we could set it to false if all of our slots store 680 # Gecko-interface-typed things, because we don't use Xray expando 681 # slots for those. But note that we would need to check the types 682 # of not only the members of "interface" but also of all its 683 # ancestors, because those can have members living in our slots too. 684 # For now, do the simple thing. 685 self.wantsXrayExpandoClass = interface.totalMembersInSlots != 0 686 687 # Read the desc, and fill in the relevant defaults. 688 ifaceName = self.interface.identifier.name 689 # For generated iterator interfaces for other iterable interfaces, we 690 # just use IterableIterator as the native type, templated on the 691 # nativeType of the iterable interface. That way we can have a 692 # templated implementation for all the duplicated iterator 693 # functionality. 694 if self.interface.isIteratorInterface(): 695 itrName = self.interface.iterableInterface.identifier.name 696 itrDesc = self.getDescriptor(itrName) 697 nativeTypeDefault = iteratorNativeType(itrDesc) 698 elif self.interface.isAsyncIteratorInterface(): 699 itrName = self.interface.asyncIterableInterface.identifier.name 700 itrDesc = self.getDescriptor(itrName) 701 nativeTypeDefault = iteratorNativeType(itrDesc) 702 703 elif self.interface.isExternal(): 704 nativeTypeDefault = "nsIDOM" + ifaceName 705 else: 706 nativeTypeDefault = "mozilla::dom::" + ifaceName 707 708 self.nativeType = desc.get("nativeType", nativeTypeDefault) 709 # Now create a version of nativeType that doesn't have extra 710 # mozilla::dom:: at the beginning. 711 prettyNativeType = self.nativeType.split("::") 712 if prettyNativeType[0] == "mozilla": 713 prettyNativeType.pop(0) 714 if prettyNativeType[0] == "dom": 715 prettyNativeType.pop(0) 716 self.prettyNativeType = "::".join(prettyNativeType) 717 718 self.jsImplParent = desc.get("jsImplParent", self.nativeType) 719 720 # Do something sane for JSObject 721 if self.nativeType == "JSObject": 722 headerDefault = "js/TypeDecls.h" 723 elif self.interface.isCallback() or self.interface.isJSImplemented(): 724 # A copy of CGHeaders.getDeclarationFilename; we can't 725 # import it here, sadly. 726 # Use our local version of the header, not the exported one, so that 727 # test bindings, which don't export, will work correctly. 728 basename = os.path.basename(self.interface.filename) 729 headerDefault = basename.replace(".webidl", "Binding.h") 730 elif not self.interface.isExternal() and self.interface.getExtendedAttribute( 731 "HeaderFile" 732 ): 733 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] 734 elif ( 735 self.interface.isIteratorInterface() 736 or self.interface.isAsyncIteratorInterface() 737 ): 738 headerDefault = "mozilla/dom/IterableIterator.h" 739 else: 740 headerDefault = self.nativeType 741 headerDefault = headerDefault.replace("::", "/") + ".h" 742 self.headerFile = desc.get("headerFile", headerDefault) 743 self.headerIsDefault = self.headerFile == headerDefault 744 if self.jsImplParent == self.nativeType: 745 self.jsImplParentHeader = self.headerFile 746 else: 747 self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" 748 749 self.notflattened = desc.get("notflattened", False) 750 self.register = desc.get("register", True) 751 752 # If we're concrete, we need to crawl our ancestor interfaces and mark 753 # them as having a concrete descendant. 754 concreteDefault = ( 755 not self.interface.isExternal() 756 and not self.interface.isCallback() 757 and not self.interface.isNamespace() 758 and 759 # We're going to assume that leaf interfaces are 760 # concrete; otherwise what's the point? Also 761 # interfaces with constructors had better be 762 # concrete; otherwise how can you construct them? 763 ( 764 not self.interface.hasChildInterfaces() 765 or self.interface.ctor() is not None 766 ) 767 ) 768 769 self.concrete = desc.get("concrete", concreteDefault) 770 self.hasLegacyUnforgeableMembers = self.concrete and any( 771 MemberIsLegacyUnforgeable(m, self) for m in self.interface.members 772 ) 773 self.operations = { 774 "IndexedGetter": None, 775 "IndexedSetter": None, 776 "IndexedDeleter": None, 777 "NamedGetter": None, 778 "NamedSetter": None, 779 "NamedDeleter": None, 780 "Stringifier": None, 781 "LegacyCaller": None, 782 } 783 784 self.hasDefaultToJSON = False 785 786 # Stringifiers need to be set up whether an interface is 787 # concrete or not, because they're actually prototype methods and hence 788 # can apply to instances of descendant interfaces. Legacy callers and 789 # named/indexed operations only need to be set up on concrete 790 # interfaces, since they affect the JSClass we end up using, not the 791 # prototype object. 792 def addOperation(operation, m): 793 if not self.operations[operation]: 794 self.operations[operation] = m 795 796 # Since stringifiers go on the prototype, we only need to worry 797 # about our own stringifier, not those of our ancestor interfaces. 798 if not self.interface.isExternal(): 799 for m in self.interface.members: 800 if m.isMethod() and m.isStringifier(): 801 addOperation("Stringifier", m) 802 if m.isMethod() and m.isDefaultToJSON(): 803 self.hasDefaultToJSON = True 804 805 # We keep track of instrumente props for all non-external interfaces. 806 self.instrumentedProps = [] 807 instrumentedProps = self.interface.getExtendedAttribute("InstrumentedProps") 808 if instrumentedProps: 809 # It's actually a one-element list, with the list 810 # we want as the only element. 811 self.instrumentedProps = instrumentedProps[0] 812 813 # Check that we don't have duplicated instrumented props. 814 uniqueInstrumentedProps = set(self.instrumentedProps) 815 if len(uniqueInstrumentedProps) != len(self.instrumentedProps): 816 duplicates = [ 817 p 818 for p in uniqueInstrumentedProps 819 if self.instrumentedProps.count(p) > 1 820 ] 821 raise TypeError( 822 "Duplicated instrumented properties: %s.\n%s" 823 % (duplicates, self.interface.location) 824 ) 825 826 if self.concrete: 827 self.proxy = False 828 iface = self.interface 829 for m in iface.members: 830 # Don't worry about inheriting legacycallers either: in 831 # practice these are on most-derived prototypes. 832 if m.isMethod() and m.isLegacycaller(): 833 if not m.isIdentifierLess(): 834 raise TypeError( 835 "We don't support legacycaller with " 836 "identifier.\n%s" % m.location 837 ) 838 if len(m.signatures()) != 1: 839 raise TypeError( 840 "We don't support overloaded legacycaller.\n%s" % m.location 841 ) 842 addOperation("LegacyCaller", m) 843 844 if desc.get("hasOrdinaryObjectPrototype", False): 845 iface.setUserData("hasOrdinaryObjectPrototype", True) 846 847 while iface: 848 for m in iface.members: 849 if not m.isMethod(): 850 continue 851 852 def addIndexedOrNamedOperation(operation, m): 853 if m.isIndexed(): 854 operation = "Indexed" + operation 855 else: 856 assert m.isNamed() 857 operation = "Named" + operation 858 addOperation(operation, m) 859 860 if m.isGetter(): 861 addIndexedOrNamedOperation("Getter", m) 862 if m.isSetter(): 863 addIndexedOrNamedOperation("Setter", m) 864 if m.isDeleter(): 865 addIndexedOrNamedOperation("Deleter", m) 866 if m.isLegacycaller() and iface != self.interface: 867 raise TypeError( 868 "We don't support legacycaller on " 869 "non-leaf interface %s.\n%s" % (iface, iface.location) 870 ) 871 872 iface.setUserData("hasConcreteDescendant", True) 873 iface = iface.parent 874 875 self.proxy = ( 876 self.supportsIndexedProperties() 877 or ( 878 self.supportsNamedProperties() and not self.hasNamedPropertiesObject 879 ) 880 or self.isMaybeCrossOriginObject() 881 ) 882 883 if self.proxy: 884 if self.isMaybeCrossOriginObject() and ( 885 self.supportsIndexedProperties() or self.supportsNamedProperties() 886 ): 887 raise TypeError( 888 "We don't support named or indexed " 889 "properties on maybe-cross-origin objects. " 890 "This lets us assume that their proxy " 891 "hooks are never called via Xrays. " 892 "Fix %s.\n%s" % (self.interface, self.interface.location) 893 ) 894 895 if not self.operations["IndexedGetter"] and ( 896 self.operations["IndexedSetter"] 897 or self.operations["IndexedDeleter"] 898 ): 899 raise SyntaxError( 900 "%s supports indexed properties but does " 901 "not have an indexed getter.\n%s" 902 % (self.interface, self.interface.location) 903 ) 904 if not self.operations["NamedGetter"] and ( 905 self.operations["NamedSetter"] or self.operations["NamedDeleter"] 906 ): 907 raise SyntaxError( 908 "%s supports named properties but does " 909 "not have a named getter.\n%s" 910 % (self.interface, self.interface.location) 911 ) 912 iface = self.interface 913 while iface: 914 iface.setUserData("hasProxyDescendant", True) 915 iface = iface.parent 916 917 if desc.get("wantsQI", None) is not None: 918 self._wantsQI = desc.get("wantsQI", None) 919 self.wrapperCache = ( 920 not self.interface.isCallback() 921 and not self.interface.isIteratorInterface() 922 and not self.interface.isAsyncIteratorInterface() 923 and desc.get("wrapperCache", True) 924 ) 925 926 self.name = interface.identifier.name 927 928 # self.implicitJSContext is a list of names of methods and attributes 929 # that need a JSContext. 930 if self.interface.isJSImplemented(): 931 self.implicitJSContext = ["constructor"] 932 else: 933 self.implicitJSContext = desc.get("implicitJSContext", []) 934 assert isinstance(self.implicitJSContext, list) 935 936 self._binaryNames = {} 937 938 if not self.interface.isExternal(): 939 940 def maybeAddBinaryName(member): 941 binaryName = member.getExtendedAttribute("BinaryName") 942 if binaryName: 943 assert isinstance(binaryName, list) 944 assert len(binaryName) == 1 945 self._binaryNames.setdefault( 946 (member.identifier.name, member.isStatic()), binaryName[0] 947 ) 948 949 for member in self.interface.members: 950 if not member.isAttr() and not member.isMethod(): 951 continue 952 maybeAddBinaryName(member) 953 954 ctor = self.interface.ctor() 955 if ctor: 956 maybeAddBinaryName(ctor) 957 958 # Some default binary names for cases when nothing else got set. 959 self._binaryNames.setdefault(("__legacycaller", False), "LegacyCall") 960 self._binaryNames.setdefault(("__stringifier", False), "Stringify") 961 962 # Build the prototype chain. 963 self.prototypeChain = [] 964 self.needsMissingPropUseCounters = False 965 parent = interface 966 while parent: 967 self.needsMissingPropUseCounters = ( 968 self.needsMissingPropUseCounters 969 or parent.getExtendedAttribute("InstrumentedProps") 970 ) 971 self.prototypeChain.insert(0, parent.identifier.name) 972 parent = parent.parent 973 config.maxProtoChainLength = max( 974 config.maxProtoChainLength, len(self.prototypeChain) 975 ) 976 977 def binaryNameFor(self, name, isStatic): 978 return self._binaryNames.get((name, isStatic), name) 979 980 @property 981 def prototypeNameChain(self): 982 return [self.getDescriptor(p).name for p in self.prototypeChain] 983 984 @property 985 def parentPrototypeName(self): 986 if len(self.prototypeChain) == 1: 987 return None 988 return self.getDescriptor(self.prototypeChain[-2]).name 989 990 def hasInterfaceOrInterfacePrototypeObject(self): 991 return ( 992 self.interface.hasInterfaceObject() 993 or self.interface.hasInterfacePrototypeObject() 994 ) 995 996 def hasOrdinaryObjectPrototype(self): 997 return self.interface.getUserData("hasOrdinaryObjectPrototype", False) 998 999 @property 1000 def hasNamedPropertiesObject(self): 1001 return self.isGlobal() and self.supportsNamedProperties() 1002 1003 def getExtendedAttributes(self, member, getter=False, setter=False): 1004 def ensureValidBoolExtendedAttribute(attr, name): 1005 if attr is not None and attr is not True: 1006 raise TypeError("Unknown value for '%s': %s" % (name, attr[0])) 1007 1008 def ensureValidThrowsExtendedAttribute(attr): 1009 ensureValidBoolExtendedAttribute(attr, "Throws") 1010 1011 def ensureValidCanOOMExtendedAttribute(attr): 1012 ensureValidBoolExtendedAttribute(attr, "CanOOM") 1013 1014 def maybeAppendNeedsErrorResultToAttrs(attrs, throws): 1015 ensureValidThrowsExtendedAttribute(throws) 1016 if throws is not None: 1017 attrs.append("needsErrorResult") 1018 1019 def maybeAppendCanOOMToAttrs(attrs, canOOM): 1020 ensureValidCanOOMExtendedAttribute(canOOM) 1021 if canOOM is not None: 1022 attrs.append("canOOM") 1023 1024 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal): 1025 if ( 1026 needsSubjectPrincipal is not None 1027 and needsSubjectPrincipal is not True 1028 and needsSubjectPrincipal != ["NonSystem"] 1029 ): 1030 raise TypeError( 1031 "Unknown value for 'NeedsSubjectPrincipal': %s" 1032 % needsSubjectPrincipal[0] 1033 ) 1034 1035 if needsSubjectPrincipal is not None: 1036 attrs.append("needsSubjectPrincipal") 1037 if needsSubjectPrincipal == ["NonSystem"]: 1038 attrs.append("needsNonSystemSubjectPrincipal") 1039 1040 name = member.identifier.name 1041 throws = self.interface.isJSImplemented() or member.getExtendedAttribute( 1042 "Throws" 1043 ) 1044 canOOM = member.getExtendedAttribute("CanOOM") 1045 needsSubjectPrincipal = member.getExtendedAttribute("NeedsSubjectPrincipal") 1046 attrs = [] 1047 if name in self.implicitJSContext: 1048 attrs.append("implicitJSContext") 1049 if member.isMethod(): 1050 if self.interface.isAsyncIteratorInterface() and name == "next": 1051 attrs.append("implicitJSContext") 1052 # JSObject-returning [NewObject] methods must be fallible, 1053 # since they have to (fallibly) allocate the new JSObject. 1054 if member.getExtendedAttribute("NewObject"): 1055 if member.returnsPromise(): 1056 throws = True 1057 elif methodReturnsJSObject(member): 1058 canOOM = True 1059 maybeAppendNeedsErrorResultToAttrs(attrs, throws) 1060 maybeAppendCanOOMToAttrs(attrs, canOOM) 1061 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal) 1062 return attrs 1063 1064 assert member.isAttr() 1065 assert bool(getter) != bool(setter) 1066 if throws is None: 1067 throwsAttr = "GetterThrows" if getter else "SetterThrows" 1068 throws = member.getExtendedAttribute(throwsAttr) 1069 maybeAppendNeedsErrorResultToAttrs(attrs, throws) 1070 if canOOM is None: 1071 canOOMAttr = "GetterCanOOM" if getter else "SetterCanOOM" 1072 canOOM = member.getExtendedAttribute(canOOMAttr) 1073 maybeAppendCanOOMToAttrs(attrs, canOOM) 1074 if needsSubjectPrincipal is None: 1075 needsSubjectPrincipalAttr = ( 1076 "GetterNeedsSubjectPrincipal" 1077 if getter 1078 else "SetterNeedsSubjectPrincipal" 1079 ) 1080 needsSubjectPrincipal = member.getExtendedAttribute( 1081 needsSubjectPrincipalAttr 1082 ) 1083 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal) 1084 return attrs 1085 1086 def supportsIndexedProperties(self): 1087 return self.operations["IndexedGetter"] is not None 1088 1089 def lengthNeedsCallerType(self): 1090 """ 1091 Determine whether our length getter needs a caller type; this is needed 1092 in some indexed-getter proxy algorithms. The idea is that if our 1093 indexed getter needs a caller type, our automatically-generated Length() 1094 calls need one too. 1095 """ 1096 assert self.supportsIndexedProperties() 1097 indexedGetter = self.operations["IndexedGetter"] 1098 return indexedGetter.getExtendedAttribute("NeedsCallerType") 1099 1100 def supportsNamedProperties(self): 1101 return self.operations["NamedGetter"] is not None 1102 1103 def supportedNamesNeedCallerType(self): 1104 """ 1105 Determine whether our GetSupportedNames call needs a caller type. The 1106 idea is that if your named getter needs a caller type, then so does 1107 GetSupportedNames. 1108 """ 1109 assert self.supportsNamedProperties() 1110 namedGetter = self.operations["NamedGetter"] 1111 return namedGetter.getExtendedAttribute("NeedsCallerType") 1112 1113 def isMaybeCrossOriginObject(self): 1114 # If we're isGlobal and have cross-origin members, we're a Window, and 1115 # that's not a cross-origin object. The WindowProxy is. 1116 return ( 1117 self.concrete 1118 and self.interface.hasCrossOriginMembers 1119 and not self.isGlobal() 1120 ) 1121 1122 def needsHeaderInclude(self): 1123 """ 1124 An interface doesn't need a header file if it is not concrete, not 1125 pref-controlled, has no prototype object, has no static methods or 1126 attributes and has no parent. The parent matters because we assert 1127 things about refcounting that depend on the actual underlying type if we 1128 have a parent. 1129 1130 """ 1131 return ( 1132 self.interface.isExternal() 1133 or self.concrete 1134 or self.interface.hasInterfacePrototypeObject() 1135 or any( 1136 (m.isAttr() or m.isMethod()) and m.isStatic() 1137 for m in self.interface.members 1138 ) 1139 or self.interface.parent 1140 ) 1141 1142 def hasThreadChecks(self): 1143 # isExposedConditionally does not necessarily imply thread checks 1144 # (since at least [SecureContext] is independent of them), but we're 1145 # only used to decide whether to include nsThreadUtils.h, so we don't 1146 # worry about that. 1147 return ( 1148 self.isExposedConditionally() and not self.interface.isExposedInWindow() 1149 ) or self.interface.isExposedInSomeButNotAllWorkers() 1150 1151 def hasCEReactions(self): 1152 return any( 1153 m.getExtendedAttribute("CEReactions") for m in self.interface.members 1154 ) 1155 1156 def isExposedConditionally(self): 1157 return ( 1158 self.interface.isExposedConditionally() 1159 or self.interface.isExposedInSomeButNotAllWorkers() 1160 ) 1161 1162 def needsXrayResolveHooks(self): 1163 """ 1164 Generally, any interface with NeedResolve needs Xray 1165 resolveOwnProperty and enumerateOwnProperties hooks. But for 1166 the special case of plugin-loading elements, we do NOT want 1167 those, because we don't want to instantiate plug-ins simply 1168 due to chrome touching them and that's all those hooks do on 1169 those elements. So we special-case those here. 1170 """ 1171 return self.interface.getExtendedAttribute( 1172 "NeedResolve" 1173 ) and self.interface.identifier.name not in [ 1174 "HTMLObjectElement", 1175 "HTMLEmbedElement", 1176 ] 1177 1178 def needsXrayNamedDeleterHook(self): 1179 return self.operations["NamedDeleter"] is not None 1180 1181 def isGlobal(self): 1182 """ 1183 Returns true if this is the primary interface for a global object 1184 of some sort. 1185 """ 1186 return self.interface.getExtendedAttribute("Global") 1187 1188 @property 1189 def namedPropertiesEnumerable(self): 1190 """ 1191 Returns whether this interface should have enumerable named properties 1192 """ 1193 assert self.proxy 1194 assert self.supportsNamedProperties() 1195 iface = self.interface 1196 while iface: 1197 if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): 1198 return False 1199 iface = iface.parent 1200 return True 1201 1202 @property 1203 def registersGlobalNamesOnWindow(self): 1204 return ( 1205 self.interface.hasInterfaceObject() 1206 and self.interface.isExposedInWindow() 1207 and self.register 1208 ) 1209 1210 def getDescriptor(self, interfaceName): 1211 """ 1212 Gets the appropriate descriptor for the given interface name. 1213 """ 1214 return self.config.getDescriptor(interfaceName) 1215 1216 def getConfig(self): 1217 return self.config 1218 1219 1220 # Some utility methods 1221 def getTypesFromDescriptor(descriptor, includeArgs=True, includeReturns=True): 1222 """ 1223 Get argument and/or return types for all members of the descriptor. By 1224 default returns all argument types (which includes types of writable 1225 attributes) and all return types (which includes types of all attributes). 1226 """ 1227 assert includeArgs or includeReturns # Must want _something_. 1228 members = [m for m in descriptor.interface.members] 1229 if descriptor.interface.ctor(): 1230 members.append(descriptor.interface.ctor()) 1231 members.extend(descriptor.interface.legacyFactoryFunctions) 1232 signatures = [s for m in members if m.isMethod() for s in m.signatures()] 1233 types = [] 1234 for s in signatures: 1235 assert len(s) == 2 1236 (returnType, arguments) = s 1237 if includeReturns: 1238 types.append(returnType) 1239 if includeArgs: 1240 types.extend(a.type for a in arguments) 1241 1242 types.extend( 1243 a.type 1244 for a in members 1245 if (a.isAttr() and (includeReturns or (includeArgs and not a.readonly))) 1246 ) 1247 1248 if descriptor.interface.maplikeOrSetlikeOrIterable: 1249 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable 1250 if maplikeOrSetlikeOrIterable.isMaplike(): 1251 # The things we expand into may or may not correctly indicate in 1252 # their formal IDL types what things we have as return values. For 1253 # example, "keys" returns the moral equivalent of sequence<keyType> 1254 # but just claims to return "object". Similarly, "values" returns 1255 # the moral equivalent of sequence<valueType> but claims to return 1256 # "object". And due to bug 1155340, "get" claims to return "any" 1257 # instead of the right type. So let's just manually work around 1258 # that lack of specificity. For our arguments, we already enforce 1259 # the right types at the IDL level, so those will get picked up 1260 # correctly. 1261 assert maplikeOrSetlikeOrIterable.hasKeyType() 1262 assert maplikeOrSetlikeOrIterable.hasValueType() 1263 if includeReturns: 1264 types.append(maplikeOrSetlikeOrIterable.keyType) 1265 types.append(maplikeOrSetlikeOrIterable.valueType) 1266 elif maplikeOrSetlikeOrIterable.isSetlike(): 1267 assert maplikeOrSetlikeOrIterable.hasKeyType() 1268 assert maplikeOrSetlikeOrIterable.hasValueType() 1269 assert ( 1270 maplikeOrSetlikeOrIterable.keyType 1271 == maplikeOrSetlikeOrIterable.valueType 1272 ) 1273 # As in the maplike case, we don't always declare our return values 1274 # quite correctly. 1275 if includeReturns: 1276 types.append(maplikeOrSetlikeOrIterable.keyType) 1277 else: 1278 assert ( 1279 maplikeOrSetlikeOrIterable.isIterable() 1280 or maplikeOrSetlikeOrIterable.isAsyncIterable() 1281 ) 1282 # As in the maplike/setlike cases we don't do a good job of 1283 # declaring our actual return types, while our argument types, if 1284 # any, are declared fine. 1285 if includeReturns: 1286 if maplikeOrSetlikeOrIterable.hasKeyType(): 1287 types.append(maplikeOrSetlikeOrIterable.keyType) 1288 if maplikeOrSetlikeOrIterable.hasValueType(): 1289 types.append(maplikeOrSetlikeOrIterable.valueType) 1290 1291 return types 1292 1293 1294 def getTypesFromDictionary(dictionary): 1295 """ 1296 Get all member types for this dictionary 1297 """ 1298 return [m.type for m in dictionary.members] 1299 1300 1301 def getTypesFromCallback(callback): 1302 """ 1303 Get the types this callback depends on: its return type and the 1304 types of its arguments. 1305 """ 1306 sig = callback.signatures()[0] 1307 types = [sig[0]] # Return type 1308 types.extend(arg.type for arg in sig[1]) # Arguments 1309 return types 1310 1311 1312 def getAllTypes(descriptors, dictionaries, callbacks): 1313 """ 1314 Generate all the types we're dealing with. For each type, a tuple 1315 containing type, dictionary is yielded. The dictionary can be None if the 1316 type does not come from a dictionary. 1317 """ 1318 for d in descriptors: 1319 if d.interface.isExternal(): 1320 continue 1321 for t in getTypesFromDescriptor(d): 1322 yield (t, None) 1323 for dictionary in dictionaries: 1324 for t in getTypesFromDictionary(dictionary): 1325 yield (t, dictionary) 1326 for callback in callbacks: 1327 for t in getTypesFromCallback(callback): 1328 yield (t, callback) 1329 1330 1331 # For sync value iterators, we use default array implementation, for async 1332 # iterators and sync pair iterators, we use AsyncIterableIterator or 1333 # IterableIterator instead. 1334 def iteratorNativeType(descriptor): 1335 assert descriptor.interface.isIterable() or descriptor.interface.isAsyncIterable() 1336 iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable 1337 assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable() 1338 if descriptor.interface.isIterable(): 1339 return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType 1340 needReturnMethod = toStringBool( 1341 descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute( 1342 "GenerateReturnMethod" 1343 ) 1344 is not None 1345 ) 1346 return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % ( 1347 descriptor.nativeType, 1348 needReturnMethod, 1349 ) 1350 1351 1352 def findInnermostType(t): 1353 """ 1354 Find the innermost type of the given type, unwrapping Promise and Record 1355 types, as well as everything that unroll() unwraps. 1356 """ 1357 while True: 1358 if t.isRecord(): 1359 t = t.inner 1360 elif t.unroll() != t: 1361 t = t.unroll() 1362 elif t.isPromise(): 1363 t = t.promiseInnerType() 1364 else: 1365 return t 1366 1367 1368 def getDependentDictionariesFromDictionary(d): 1369 """ 1370 Find all the dictionaries contained in the given dictionary, as ancestors or 1371 members. This returns a generator. 1372 """ 1373 while d: 1374 yield d 1375 for member in d.members: 1376 yield from getDictionariesFromType(member.type) 1377 d = d.parent 1378 1379 1380 def getDictionariesFromType(type): 1381 """ 1382 Find all the dictionaries contained in type. This can be used to find 1383 dictionaries that need conversion to JS (by looking at types that get 1384 converted to JS) or dictionaries that need conversion from JS (by looking at 1385 types that get converted from JS). 1386 1387 This returns a generator. 1388 """ 1389 type = findInnermostType(type) 1390 if type.isUnion(): 1391 # Look for dictionaries in all the member types 1392 for t in type.flatMemberTypes: 1393 yield from getDictionariesFromType(t) 1394 elif type.isDictionary(): 1395 # Find the dictionaries that are itself, any of its ancestors, or 1396 # contained in any of its member types. 1397 yield from getDependentDictionariesFromDictionary(type.inner) 1398 1399 1400 def getDictionariesConvertedToJS(descriptors, dictionaries, callbacks): 1401 for desc in descriptors: 1402 if desc.interface.isExternal(): 1403 continue 1404 1405 if desc.interface.isJSImplemented(): 1406 # For a JS-implemented interface, we need to-JS 1407 # conversions for all the types involved. 1408 for t in getTypesFromDescriptor(desc): 1409 for d in getDictionariesFromType(t): 1410 yield d 1411 elif desc.interface.isCallback(): 1412 # For callbacks we only want to include the arguments, since that's 1413 # where the to-JS conversion happens. 1414 for t in getTypesFromDescriptor(desc, includeReturns=False): 1415 for d in getDictionariesFromType(t): 1416 yield d 1417 else: 1418 # For normal interfaces, we only want to include return values, 1419 # since that's where to-JS conversion happens. 1420 for t in getTypesFromDescriptor(desc, includeArgs=False): 1421 for d in getDictionariesFromType(t): 1422 yield d 1423 1424 for callback in callbacks: 1425 # We only want to look at the arguments 1426 sig = callback.signatures()[0] 1427 for arg in sig[1]: 1428 for d in getDictionariesFromType(arg.type): 1429 yield d 1430 1431 for dictionary in dictionaries: 1432 if dictionary.needsConversionToJS: 1433 # It's explicitly flagged as needing to-JS conversion, and all its 1434 # dependent dictionaries will need to-JS conversion too. 1435 for d in getDependentDictionariesFromDictionary(dictionary): 1436 yield d 1437 1438 1439 def getDictionariesConvertedFromJS(descriptors, dictionaries, callbacks): 1440 for desc in descriptors: 1441 if desc.interface.isExternal(): 1442 continue 1443 1444 if desc.interface.isJSImplemented(): 1445 # For a JS-implemented interface, we need from-JS conversions for 1446 # all the types involved. 1447 for t in getTypesFromDescriptor(desc): 1448 for d in getDictionariesFromType(t): 1449 yield d 1450 elif desc.interface.isCallback(): 1451 # For callbacks we only want to include the return value, since 1452 # that's where teh from-JS conversion happens. 1453 for t in getTypesFromDescriptor(desc, includeArgs=False): 1454 for d in getDictionariesFromType(t): 1455 yield d 1456 else: 1457 # For normal interfaces, we only want to include arguments values, 1458 # since that's where from-JS conversion happens. 1459 for t in getTypesFromDescriptor(desc, includeReturns=False): 1460 for d in getDictionariesFromType(t): 1461 yield d 1462 1463 for callback in callbacks: 1464 # We only want to look at the return value 1465 sig = callback.signatures()[0] 1466 for d in getDictionariesFromType(sig[0]): 1467 yield d 1468 1469 for dictionary in dictionaries: 1470 if dictionary.needsConversionFromJS: 1471 # It's explicitly flagged as needing from-JS conversion, and all its 1472 # dependent dictionaries will need from-JS conversion too. 1473 for d in getDependentDictionariesFromDictionary(dictionary): 1474 yield d