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 5import os 6 7from WebIDL import IDLExternalInterface, IDLSequenceType, IDLWrapperType, WebIDLError 8 9 10class Configuration: 11 """ 12 Represents global configuration state based on IDL parse data and 13 the configuration file. 14 """ 15 def __init__(self, filename, parseData): 16 # Read the configuration file. 17 glbl = {} 18 execfile(filename, glbl) 19 config = glbl['DOMInterfaces'] 20 21 # Build descriptors for all the interfaces we have in the parse data. 22 # This allows callers to specify a subset of interfaces by filtering 23 # |parseData|. 24 self.descriptors = [] 25 self.interfaces = {} 26 self.maxProtoChainLength = 0 27 for thing in parseData: 28 # Servo does not support external interfaces. 29 if isinstance(thing, IDLExternalInterface): 30 raise WebIDLError("Servo does not support external interfaces.", 31 [thing.location]) 32 33 assert not thing.isType() 34 35 if not thing.isInterface() and not thing.isNamespace(): 36 continue 37 38 iface = thing 39 self.interfaces[iface.identifier.name] = iface 40 if iface.identifier.name not in config: 41 # Completely skip consequential interfaces with no descriptor 42 # if they have no interface object because chances are we 43 # don't need to do anything interesting with them. 44 if iface.isConsequential() and not iface.hasInterfaceObject(): 45 continue 46 entry = {} 47 else: 48 entry = config[iface.identifier.name] 49 if not isinstance(entry, list): 50 assert isinstance(entry, dict) 51 entry = [entry] 52 self.descriptors.extend( 53 [Descriptor(self, iface, x) for x in entry]) 54 55 # Mark the descriptors for which only a single nativeType implements 56 # an interface. 57 for descriptor in self.descriptors: 58 intefaceName = descriptor.interface.identifier.name 59 otherDescriptors = [d for d in self.descriptors 60 if d.interface.identifier.name == intefaceName] 61 descriptor.uniqueImplementation = len(otherDescriptors) == 1 62 63 self.enums = [e for e in parseData if e.isEnum()] 64 self.typedefs = [e for e in parseData if e.isTypedef()] 65 self.dictionaries = [d for d in parseData if d.isDictionary()] 66 self.callbacks = [c for c in parseData if 67 c.isCallback() and not c.isInterface()] 68 69 # Keep the descriptor list sorted for determinism. 70 self.descriptors.sort(lambda x, y: cmp(x.name, y.name)) 71 72 def getInterface(self, ifname): 73 return self.interfaces[ifname] 74 75 def getDescriptors(self, **filters): 76 """Gets the descriptors that match the given filters.""" 77 curr = self.descriptors 78 for key, val in filters.iteritems(): 79 if key == 'webIDLFile': 80 getter = lambda x: x.interface.filename() 81 elif key == 'hasInterfaceObject': 82 getter = lambda x: x.interface.hasInterfaceObject() 83 elif key == 'isCallback': 84 getter = lambda x: x.interface.isCallback() 85 elif key == 'isNamespace': 86 getter = lambda x: x.interface.isNamespace() 87 elif key == 'isJSImplemented': 88 getter = lambda x: x.interface.isJSImplemented() 89 elif key == 'isGlobal': 90 getter = lambda x: x.isGlobal() 91 elif key == 'isInline': 92 getter = lambda x: x.interface.getExtendedAttribute('Inline') is not None 93 elif key == 'isExposedConditionally': 94 getter = lambda x: x.interface.isExposedConditionally() 95 elif key == 'isIteratorInterface': 96 getter = lambda x: x.interface.isIteratorInterface() 97 else: 98 getter = lambda x: getattr(x, key) 99 curr = filter(lambda x: getter(x) == val, curr) 100 return curr 101 102 def getEnums(self, webIDLFile): 103 return filter(lambda e: e.filename() == webIDLFile, self.enums) 104 105 def getTypedefs(self, webIDLFile): 106 return filter(lambda e: e.filename() == webIDLFile, self.typedefs) 107 108 @staticmethod 109 def _filterForFile(items, webIDLFile=""): 110 """Gets the items that match the given filters.""" 111 if not webIDLFile: 112 return items 113 114 return filter(lambda x: x.filename() == webIDLFile, items) 115 116 def getDictionaries(self, webIDLFile=""): 117 return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile) 118 119 def getCallbacks(self, webIDLFile=""): 120 return self._filterForFile(self.callbacks, webIDLFile=webIDLFile) 121 122 def getDescriptor(self, interfaceName): 123 """ 124 Gets the appropriate descriptor for the given interface name. 125 """ 126 iface = self.getInterface(interfaceName) 127 descriptors = self.getDescriptors(interface=iface) 128 129 # We should have exactly one result. 130 if len(descriptors) != 1: 131 raise NoSuchDescriptorError("For " + interfaceName + " found " + 132 str(len(descriptors)) + " matches") 133 return descriptors[0] 134 135 def getDescriptorProvider(self): 136 """ 137 Gets a descriptor provider that can provide descriptors as needed. 138 """ 139 return DescriptorProvider(self) 140 141 142class NoSuchDescriptorError(TypeError): 143 def __init__(self, str): 144 TypeError.__init__(self, str) 145 146 147class DescriptorProvider: 148 """ 149 A way of getting descriptors for interface names 150 """ 151 def __init__(self, config): 152 self.config = config 153 154 def getDescriptor(self, interfaceName): 155 """ 156 Gets the appropriate descriptor for the given interface name given the 157 context of the current descriptor. 158 """ 159 return self.config.getDescriptor(interfaceName) 160 161 162def MemberIsUnforgeable(member, descriptor): 163 return ((member.isAttr() or member.isMethod()) and 164 not member.isStatic() and 165 (member.isUnforgeable() or 166 bool(descriptor.interface.getExtendedAttribute("Unforgeable")))) 167 168 169class Descriptor(DescriptorProvider): 170 """ 171 Represents a single descriptor for an interface. See Bindings.conf. 172 """ 173 def __init__(self, config, interface, desc): 174 DescriptorProvider.__init__(self, config) 175 self.interface = interface 176 177 if not self.isExposedConditionally(): 178 if interface.parent and interface.parent.isExposedConditionally(): 179 raise TypeError("%s is not conditionally exposed but inherits from " 180 "%s which is" % 181 (interface.identifier.name, interface.parent.identifier.name)) 182 183 # Read the desc, and fill in the relevant defaults. 184 ifaceName = self.interface.identifier.name 185 nativeTypeDefault = ifaceName 186 187 # For generated iterator interfaces for other iterable interfaces, we 188 # just use IterableIterator as the native type, templated on the 189 # nativeType of the iterable interface. That way we can have a 190 # templated implementation for all the duplicated iterator 191 # functionality. 192 if self.interface.isIteratorInterface(): 193 itrName = self.interface.iterableInterface.identifier.name 194 itrDesc = self.getDescriptor(itrName) 195 nativeTypeDefault = iteratorNativeType(itrDesc) 196 197 typeName = desc.get('nativeType', nativeTypeDefault) 198 199 spiderMonkeyInterface = desc.get('spiderMonkeyInterface', False) 200 201 # Callback and SpiderMonkey types do not use JS smart pointers, so we should not use the 202 # built-in rooting mechanisms for them. 203 if spiderMonkeyInterface: 204 self.returnType = 'Rc<%s>' % typeName 205 self.argumentType = '&%s' % typeName 206 self.nativeType = typeName 207 pathDefault = 'dom::types::%s' % typeName 208 elif self.interface.isCallback(): 209 ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName) 210 pathDefault = ty 211 self.returnType = "Rc<%s>" % ty 212 self.argumentType = "???" 213 self.nativeType = ty 214 else: 215 self.returnType = "DomRoot<%s>" % typeName 216 self.argumentType = "&%s" % typeName 217 self.nativeType = "*const %s" % typeName 218 if self.interface.isIteratorInterface(): 219 pathDefault = 'dom::bindings::iterable::IterableIterator' 220 else: 221 pathDefault = 'dom::types::%s' % MakeNativeName(typeName) 222 223 self.concreteType = typeName 224 self.register = desc.get('register', True) 225 self.path = desc.get('path', pathDefault) 226 self.bindingPath = 'dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2)) 227 self.outerObjectHook = desc.get('outerObjectHook', 'None') 228 self.proxy = False 229 self.weakReferenceable = desc.get('weakReferenceable', False) 230 231 # If we're concrete, we need to crawl our ancestor interfaces and mark 232 # them as having a concrete descendant. 233 self.concrete = (not self.interface.isCallback() and 234 not self.interface.isNamespace() and 235 not self.interface.getExtendedAttribute("Abstract") and 236 not self.interface.getExtendedAttribute("Inline") and 237 not spiderMonkeyInterface) 238 self.hasUnforgeableMembers = (self.concrete and 239 any(MemberIsUnforgeable(m, self) for m in 240 self.interface.members)) 241 242 self.operations = { 243 'IndexedGetter': None, 244 'IndexedSetter': None, 245 'IndexedDeleter': None, 246 'NamedGetter': None, 247 'NamedSetter': None, 248 'NamedDeleter': None, 249 'Stringifier': None, 250 } 251 252 def addOperation(operation, m): 253 if not self.operations[operation]: 254 self.operations[operation] = m 255 256 # Since stringifiers go on the prototype, we only need to worry 257 # about our own stringifier, not those of our ancestor interfaces. 258 for m in self.interface.members: 259 if m.isMethod() and m.isStringifier(): 260 addOperation('Stringifier', m) 261 262 if self.concrete: 263 iface = self.interface 264 while iface: 265 for m in iface.members: 266 if not m.isMethod(): 267 continue 268 269 def addIndexedOrNamedOperation(operation, m): 270 self.proxy = True 271 if m.isIndexed(): 272 operation = 'Indexed' + operation 273 else: 274 assert m.isNamed() 275 operation = 'Named' + operation 276 addOperation(operation, m) 277 278 if m.isGetter(): 279 addIndexedOrNamedOperation('Getter', m) 280 if m.isSetter(): 281 addIndexedOrNamedOperation('Setter', m) 282 if m.isCreator(): 283 addIndexedOrNamedOperation('Creator', m) 284 if m.isDeleter(): 285 addIndexedOrNamedOperation('Deleter', m) 286 287 iface = iface.parent 288 if iface: 289 iface.setUserData('hasConcreteDescendant', True) 290 291 if self.proxy: 292 iface = self.interface 293 while iface.parent: 294 iface = iface.parent 295 iface.setUserData('hasProxyDescendant', True) 296 297 self.name = interface.identifier.name 298 299 # self.extendedAttributes is a dict of dicts, keyed on 300 # all/getterOnly/setterOnly and then on member name. Values are an 301 # array of extended attributes. 302 self.extendedAttributes = {'all': {}, 'getterOnly': {}, 'setterOnly': {}} 303 304 def addExtendedAttribute(attribute, config): 305 def add(key, members, attribute): 306 for member in members: 307 self.extendedAttributes[key].setdefault(member, []).append(attribute) 308 309 if isinstance(config, dict): 310 for key in ['all', 'getterOnly', 'setterOnly']: 311 add(key, config.get(key, []), attribute) 312 elif isinstance(config, list): 313 add('all', config, attribute) 314 else: 315 assert isinstance(config, str) 316 if config == '*': 317 iface = self.interface 318 while iface: 319 add('all', map(lambda m: m.name, iface.members), attribute) 320 iface = iface.parent 321 else: 322 add('all', [config], attribute) 323 324 self._binaryNames = desc.get('binaryNames', {}) 325 self._binaryNames.setdefault('__legacycaller', 'LegacyCall') 326 self._binaryNames.setdefault('__stringifier', 'Stringifier') 327 328 self._internalNames = desc.get('internalNames', {}) 329 330 for member in self.interface.members: 331 if not member.isAttr() and not member.isMethod(): 332 continue 333 binaryName = member.getExtendedAttribute("BinaryName") 334 if binaryName: 335 assert isinstance(binaryName, list) 336 assert len(binaryName) == 1 337 self._binaryNames.setdefault(member.identifier.name, 338 binaryName[0]) 339 self._internalNames.setdefault(member.identifier.name, 340 member.identifier.name.replace('-', '_')) 341 342 # Build the prototype chain. 343 self.prototypeChain = [] 344 parent = interface 345 while parent: 346 self.prototypeChain.insert(0, parent.identifier.name) 347 parent = parent.parent 348 self.prototypeDepth = len(self.prototypeChain) - 1 349 config.maxProtoChainLength = max(config.maxProtoChainLength, 350 len(self.prototypeChain)) 351 352 def binaryNameFor(self, name): 353 return self._binaryNames.get(name, name) 354 355 def internalNameFor(self, name): 356 return self._internalNames.get(name, name) 357 358 def getExtendedAttributes(self, member, getter=False, setter=False): 359 def maybeAppendInfallibleToAttrs(attrs, throws): 360 if throws is None: 361 attrs.append("infallible") 362 elif throws is True: 363 pass 364 else: 365 raise TypeError("Unknown value for 'Throws'") 366 367 name = member.identifier.name 368 if member.isMethod(): 369 attrs = self.extendedAttributes['all'].get(name, []) 370 throws = member.getExtendedAttribute("Throws") 371 maybeAppendInfallibleToAttrs(attrs, throws) 372 return attrs 373 374 assert member.isAttr() 375 assert bool(getter) != bool(setter) 376 key = 'getterOnly' if getter else 'setterOnly' 377 attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, []) 378 throws = member.getExtendedAttribute("Throws") 379 if throws is None: 380 throwsAttr = "GetterThrows" if getter else "SetterThrows" 381 throws = member.getExtendedAttribute(throwsAttr) 382 maybeAppendInfallibleToAttrs(attrs, throws) 383 return attrs 384 385 def getParentName(self): 386 parent = self.interface.parent 387 while parent: 388 if not parent.getExtendedAttribute("Inline"): 389 return parent.identifier.name 390 parent = parent.parent 391 return None 392 393 def hasDescendants(self): 394 return (self.interface.getUserData("hasConcreteDescendant", False) or 395 self.interface.getUserData("hasProxyDescendant", False)) 396 397 def shouldHaveGetConstructorObjectMethod(self): 398 assert self.interface.hasInterfaceObject() 399 if self.interface.getExtendedAttribute("Inline"): 400 return False 401 return (self.interface.isCallback() or self.interface.isNamespace() or 402 self.hasDescendants() or self.interface.getExtendedAttribute("HTMLConstructor")) 403 404 def shouldCacheConstructor(self): 405 return self.hasDescendants() or self.interface.getExtendedAttribute("HTMLConstructor") 406 407 def isExposedConditionally(self): 408 return self.interface.isExposedConditionally() 409 410 def isGlobal(self): 411 """ 412 Returns true if this is the primary interface for a global object 413 of some sort. 414 """ 415 return bool(self.interface.getExtendedAttribute("Global") or 416 self.interface.getExtendedAttribute("PrimaryGlobal")) 417 418 419# Some utility methods 420 421 422def MakeNativeName(name): 423 return name[0].upper() + name[1:] 424 425 426def getModuleFromObject(object): 427 return ('dom::bindings::codegen::Bindings::' + 428 os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding') 429 430 431def getTypesFromDescriptor(descriptor): 432 """ 433 Get all argument and return types for all members of the descriptor 434 """ 435 members = [m for m in descriptor.interface.members] 436 if descriptor.interface.ctor(): 437 members.append(descriptor.interface.ctor()) 438 members.extend(descriptor.interface.namedConstructors) 439 signatures = [s for m in members if m.isMethod() for s in m.signatures()] 440 types = [] 441 for s in signatures: 442 assert len(s) == 2 443 (returnType, arguments) = s 444 types.append(returnType) 445 types.extend(a.type for a in arguments) 446 447 types.extend(a.type for a in members if a.isAttr()) 448 return types 449 450 451def getTypesFromDictionary(dictionary): 452 """ 453 Get all member types for this dictionary 454 """ 455 if isinstance(dictionary, IDLWrapperType): 456 dictionary = dictionary.inner 457 types = [] 458 curDict = dictionary 459 while curDict: 460 types.extend([getUnwrappedType(m.type) for m in curDict.members]) 461 curDict = curDict.parent 462 return types 463 464 465def getTypesFromCallback(callback): 466 """ 467 Get the types this callback depends on: its return type and the 468 types of its arguments. 469 """ 470 sig = callback.signatures()[0] 471 types = [sig[0]] # Return type 472 types.extend(arg.type for arg in sig[1]) # Arguments 473 return types 474 475 476def getUnwrappedType(type): 477 while isinstance(type, IDLSequenceType): 478 type = type.inner 479 return type 480 481 482def iteratorNativeType(descriptor, infer=False): 483 assert descriptor.interface.isIterable() 484 iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable 485 assert iterableDecl.isPairIterator() 486 return "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name) 487