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