1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""IDL type handling.
5
6Classes:
7IdlTypeBase
8 IdlType
9 IdlUnionType
10 IdlArrayOrSequenceType
11  IdlArrayType
12  IdlSequenceType
13 IdlNullableType
14"""
15
16from collections import defaultdict
17
18
19################################################################################
20# IDL types
21################################################################################
22
23INTEGER_TYPES = frozenset([
24    # http://www.w3.org/TR/WebIDL/#dfn-integer-type
25    'byte',
26    'octet',
27    'short',
28    'unsigned short',
29    # int and unsigned are not IDL types
30    'long',
31    'unsigned long',
32    'long long',
33    'unsigned long long',
34])
35NUMERIC_TYPES = (INTEGER_TYPES | frozenset([
36    # http://www.w3.org/TR/WebIDL/#dfn-numeric-type
37    'float',
38    'unrestricted float',
39    'double',
40    'unrestricted double',
41]))
42# http://www.w3.org/TR/WebIDL/#dfn-primitive-type
43PRIMITIVE_TYPES = (frozenset(['boolean']) | NUMERIC_TYPES)
44BASIC_TYPES = (PRIMITIVE_TYPES | frozenset([
45    # Built-in, non-composite, non-object data types
46    # http://heycam.github.io/webidl/#idl-types
47    'DOMString',
48    'ByteString',
49    'Date',
50    # http://heycam.github.io/webidl/#es-type-mapping
51    'void',
52    # http://encoding.spec.whatwg.org/#type-scalarvaluestring
53    'ScalarValueString',
54]))
55TYPE_NAMES = {
56    # http://heycam.github.io/webidl/#dfn-type-name
57    'any': 'Any',
58    'boolean': 'Boolean',
59    'byte': 'Byte',
60    'octet': 'Octet',
61    'short': 'Short',
62    'unsigned short': 'UnsignedShort',
63    'long': 'Long',
64    'unsigned long': 'UnsignedLong',
65    'long long': 'LongLong',
66    'unsigned long long': 'UnsignedLongLong',
67    'float': 'Float',
68    'unrestricted float': 'UnrestrictedFloat',
69    'double': 'Double',
70    'unrestricted double': 'UnrestrictedDouble',
71    'DOMString': 'String',
72    'ByteString': 'ByteString',
73    'ScalarValueString': 'ScalarValueString',
74    'object': 'Object',
75    'Date': 'Date',
76}
77
78STRING_TYPES = frozenset([
79    # http://heycam.github.io/webidl/#es-interface-call (step 10.11)
80    # (Interface object [[Call]] method's string types.)
81    'String',
82    'ByteString',
83    'ScalarValueString',
84])
85
86
87################################################################################
88# Inheritance
89################################################################################
90
91ancestors = defaultdict(list)  # interface_name -> ancestors
92
93def inherits_interface(interface_name, ancestor_name):
94    return (interface_name == ancestor_name or
95            ancestor_name in ancestors[interface_name])
96
97
98def set_ancestors(new_ancestors):
99    ancestors.update(new_ancestors)
100
101
102class IdlTypeBase(object):
103    """Base class for IdlType, IdlUnionType, IdlArrayOrSequenceType and IdlNullableType."""
104
105    def __str__(self):
106        raise NotImplementedError(
107            '__str__() should be defined in subclasses')
108
109    def __getattr__(self, name):
110        # Default undefined attributes to None (analogous to Jinja variables).
111        # This allows us to not define default properties in the base class, and
112        # allows us to relay __getattr__ in IdlNullableType to the inner type.
113        return None
114
115    def resolve_typedefs(self, typedefs):
116        raise NotImplementedError(
117            'resolve_typedefs should be defined in subclasses')
118
119
120################################################################################
121# IdlType
122################################################################################
123
124class IdlType(IdlTypeBase):
125    # FIXME: incorporate Nullable, etc.
126    # to support types like short?[] vs. short[]?, instead of treating these
127    # as orthogonal properties (via flags).
128    callback_functions = set()
129    callback_interfaces = set()
130    dictionaries = set()
131    enums = {}  # name -> values
132
133    def __init__(self, base_type, is_unrestricted=False):
134        super(IdlType, self).__init__()
135        if is_unrestricted:
136            self.base_type = 'unrestricted %s' % base_type
137        else:
138            self.base_type = base_type
139
140    def __str__(self):
141        return self.base_type
142
143    @property
144    def is_basic_type(self):
145        return self.base_type in BASIC_TYPES
146
147    @property
148    def is_callback_function(self):
149        return self.base_type in IdlType.callback_functions
150
151    @property
152    def is_callback_interface(self):
153        return self.base_type in IdlType.callback_interfaces
154
155    @property
156    def is_dictionary(self):
157        return self.base_type in IdlType.dictionaries
158
159    @property
160    def is_enum(self):
161        # FIXME: add an IdlEnumType class and a resolve_enums step at end of
162        # IdlDefinitions constructor
163        return self.name in IdlType.enums
164
165    @property
166    def enum_values(self):
167        return IdlType.enums[self.name]
168
169    @property
170    def is_integer_type(self):
171        return self.base_type in INTEGER_TYPES
172
173    @property
174    def is_numeric_type(self):
175        return self.base_type in NUMERIC_TYPES
176
177    @property
178    def is_primitive_type(self):
179        return self.base_type in PRIMITIVE_TYPES
180
181    @property
182    def is_interface_type(self):
183        # Anything that is not another type is an interface type.
184        # http://www.w3.org/TR/WebIDL/#idl-types
185        # http://www.w3.org/TR/WebIDL/#idl-interface
186        # In C++ these are RefPtr or PassRefPtr types.
187        return not(self.is_basic_type or
188                   self.is_callback_function or
189                   self.is_dictionary or
190                   self.is_enum or
191                   self.name == 'Any' or
192                   self.name == 'Object' or
193                   self.name == 'Promise')  # Promise will be basic in future
194
195    @property
196    def is_string_type(self):
197        return self.name in STRING_TYPES
198
199    @property
200    def is_union_type(self):
201        return isinstance(self, IdlUnionType)
202
203    @property
204    def name(self):
205        """Return type name
206
207        http://heycam.github.io/webidl/#dfn-type-name
208        """
209        base_type = self.base_type
210        return TYPE_NAMES.get(base_type, base_type)
211
212    @classmethod
213    def set_callback_functions(cls, new_callback_functions):
214        cls.callback_functions.update(new_callback_functions)
215
216    @classmethod
217    def set_callback_interfaces(cls, new_callback_interfaces):
218        cls.callback_interfaces.update(new_callback_interfaces)
219
220    @classmethod
221    def set_dictionaries(cls, new_dictionaries):
222        cls.dictionaries.update(new_dictionaries)
223
224    @classmethod
225    def set_enums(cls, new_enums):
226        cls.enums.update(new_enums)
227
228    def resolve_typedefs(self, typedefs):
229        # This function either returns |self| or a different object.
230        # FIXME: Rename typedefs_resolved().
231        return typedefs.get(self.base_type, self)
232
233
234################################################################################
235# IdlUnionType
236################################################################################
237
238class IdlUnionType(IdlTypeBase):
239    # http://heycam.github.io/webidl/#idl-union
240    def __init__(self, member_types):
241        super(IdlUnionType, self).__init__()
242        self.member_types = member_types
243
244    @property
245    def is_union_type(self):
246        return True
247
248    @property
249    def name(self):
250        """Return type name (or inner type name if nullable)
251
252        http://heycam.github.io/webidl/#dfn-type-name
253        """
254        return 'Or'.join(member_type.name for member_type in self.member_types)
255
256    def resolve_typedefs(self, typedefs):
257        self.member_types = [
258            typedefs.get(member_type, member_type)
259            for member_type in self.member_types]
260        return self
261
262
263################################################################################
264# IdlArrayOrSequenceType, IdlArrayType, IdlSequenceType
265################################################################################
266
267class IdlArrayOrSequenceType(IdlTypeBase):
268    """Base class for IdlArrayType and IdlSequenceType."""
269
270    def __init__(self, element_type):
271        super(IdlArrayOrSequenceType, self).__init__()
272        self.element_type = element_type
273
274    def resolve_typedefs(self, typedefs):
275        self.element_type = self.element_type.resolve_typedefs(typedefs)
276        return self
277
278
279class IdlArrayType(IdlArrayOrSequenceType):
280    def __init__(self, element_type):
281        super(IdlArrayType, self).__init__(element_type)
282
283    def __str__(self):
284        return '%s[]' % self.element_type
285
286    @property
287    def name(self):
288        return self.element_type.name + 'Array'
289
290
291class IdlSequenceType(IdlArrayOrSequenceType):
292    def __init__(self, element_type):
293        super(IdlSequenceType, self).__init__(element_type)
294
295    def __str__(self):
296        return 'sequence<%s>' % self.element_type
297
298    @property
299    def name(self):
300        return self.element_type.name + 'Sequence'
301
302
303################################################################################
304# IdlNullableType
305################################################################################
306
307class IdlNullableType(IdlTypeBase):
308    def __init__(self, inner_type):
309        super(IdlNullableType, self).__init__()
310        self.inner_type = inner_type
311
312    def __str__(self):
313        # FIXME: Dictionary::ConversionContext::setConversionType can't
314        # handle the '?' in nullable types (passes nullability separately).
315        # Update that function to handle nullability from the type name,
316        # simplifying its signature.
317        # return str(self.inner_type) + '?'
318        return str(self.inner_type)
319
320    def __getattr__(self, name):
321        return getattr(self.inner_type, name)
322
323    @property
324    def is_nullable(self):
325        return True
326
327    @property
328    def name(self):
329        return self.inner_type.name + 'OrNull'
330
331    def resolve_typedefs(self, typedefs):
332        self.inner_type = self.inner_type.resolve_typedefs(typedefs)
333        return self
334